diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 00000000000..d3643eda344 --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +github: + description: "Apache Commons Lang" + homepage: https://commons.apache.org/lang/ + +notifications: + commits: commits@commons.apache.org + issues: issues@commons.apache.org + pullrequests: issues@commons.apache.org + jira_options: link label + jobs: notifications@commons.apache.org + # commits_bot_dependabot: dependabot@commons.apache.org + issues_bot_dependabot: dependabot@commons.apache.org + pullrequests_bot_dependabot: dependabot@commons.apache.org + issues_bot_codecov-commenter: notifications@commons.apache.org + pullrequests_bot_codecov-commenter: notifications@commons.apache.org diff --git a/.gitattributes b/.gitattributes index a3546495c96..715c926ab11 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,7 +5,7 @@ # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, diff --git a/.travis.yml b/.github/GH-ROBOTS.txt similarity index 67% rename from .travis.yml rename to .github/GH-ROBOTS.txt index f80bbe83319..64a88674fe4 100644 --- a/.travis.yml +++ b/.github/GH-ROBOTS.txt @@ -5,7 +5,7 @@ # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -13,22 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -language: java -jdk: - - openjdk8 - - openjdk11 - - openjdk13 - - openjdk-ea - -matrix: - include: - - os: linux-ppc64le - jdk: openjdk8 - allow_failures: - - jdk: openjdk-ea - -script: - - mvn - -after_success: - - mvn clean test jacoco:report coveralls:report -Ptravis-jacoco javadoc:javadoc -Ddoclint=all +# Keeps on creating FUD PRs in test code +# Does not follow Apache disclosure policies +User-agent: JLLeitschuh/security-research +Disallow: * diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..08d5e7de792 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "quarterly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "quarterly" + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..9ff35c83e79 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,30 @@ + + +Thanks for your contribution to [Apache Commons](https://commons.apache.org/)! Your help is appreciated! + +Before you push a pull request, review this list: + +- [ ] Read the [contribution guidelines](CONTRIBUTING.md) for this project. +- [ ] Read the [ASF Generative Tooling Guidance](https://www.apache.org/legal/generative-tooling.html) if you use Artificial Intelligence (AI). +- [ ] I used AI to create any part of, or all of, this pull request. Which AI tool was used to create this pull request, and to what extent did it contribute? +- [ ] Run a successful build using the default [Maven](https://maven.apache.org/) goal with `mvn`; that's `mvn` on the command line by itself. +- [ ] Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible, but it is a best practice. +- [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. +- [ ] Each commit in the pull request should have a meaningful subject line and body. Note that a maintainer may squash commits during the merge process. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..507f66d904f --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,86 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '33 9 * * 4' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + max-parallel: 20 + fail-fast: false + matrix: + language: [ 'java', 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000000..4c17443c92b --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: 'Dependency Review PR' + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 757411826ef..a6641b1dfc4 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,7 +5,7 @@ # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,21 +15,48 @@ name: Java CI -on: [push] +on: + push: + branches: + - 'master' + pull_request: {} + +permissions: + contents: read jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} strategy: + max-parallel: 20 matrix: - java: [ '1.8', '11.0.4', '12.0.2' ] - + os: [ubuntu-latest, windows-latest, macos-latest] + java: [ 8, 11, 17, 21, 25, 26 ] + experimental: [false] + include: + - java: 27-ea + experimental: true + os: ubuntu-latest + - java: 28-ea + experimental: true + os: ubuntu-latest + steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # 6.0.0 with: + distribution: ${{ runner.os == 'macOS' && matrix.java == '8' && 'zulu' || 'temurin' }} java-version: ${{ matrix.java }} - name: Build with Maven - run: mvn -V apache-rat:check spotbugs:check javadoc:javadoc -Ddoclint=all package --file pom.xml + run: mvn --errors --show-version --batch-mode --no-transfer-progress -Ddoclint=all diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml new file mode 100644 index 00000000000..c9b01ffcf8c --- /dev/null +++ b/.github/workflows/scorecards-analysis.yml @@ -0,0 +1,69 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache license, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the license for the specific language governing permissions and +# limitations under the license. + +name: "Scorecards supply-chain security" + +on: + branch_protection_rule: + schedule: + - cron: "30 1 * * 6" # Weekly on Saturdays + push: + branches: [ "master" ] + +permissions: read-all + +jobs: + + analysis: + + name: "Scorecards analysis" + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to the code-scanning dashboard. + security-events: write + actions: read + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + + steps: + + - name: "Checkout code" + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc #2.4.4 + with: + results_file: results.sarif + results_format: sarif + # A read-only PAT token, which is sufficient for the action to function. + # The relevant discussion: https://github.com/ossf/scorecard-action/issues/188 + repo_token: ${{ secrets.GITHUB_TOKEN }} + # Publish the results for public repositories to enable scorecard badges. + # For more details: https://github.com/ossf/scorecard-action#publishing-results + publish_results: true + + - name: "Upload artifact" + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index c30f980e360..8d85ed51cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ maven-eclipse.xml build.properties site-content *~ +.mvn/ # IntelliJ IDEA files .idea @@ -18,3 +19,11 @@ site-content .project .externalToolBuilders .checkstyle + +# jenv's version file +.java-version +/.DS_Store + +# NetBeans files +nb-configuration.xml +nbactions.xml diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..b4342f33ca5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,17 @@ + +The Apache code of conduct page is [https://www.apache.org/foundation/policies/conduct.html](https://www.apache.org/foundation/policies/conduct.html). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf6b5657134..88b72207b87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates | +======================================================================+ | | - | 1) Re-generate using: mvn commons:contributing-md | + | 1) Re-generate using: mvn commons-build:contributing-md | | | | 2) Set the following properties in the component's pom: | | - commons.jira.id (required, alphabetic, upper case) | @@ -41,38 +41,40 @@ Contributing to Apache Commons Lang ====================== -You have found a bug or you have an idea for a cool new feature? Contributing code is a great way to give something back to -the open source community. Before you dig right into the code there are a few guidelines that we need contributors to -follow so that we can have a chance of keeping on top of things. +Have you found a bug or have an idea for a cool new feature? Contributing code is a great way to give something back to the open-source community. +Before you dig right into the code, we need contributors to follow a few guidelines to have a chance of keeping on top of things. Getting Started --------------- + Make sure you have a [JIRA account](https://issues.apache.org/jira/). -+ Make sure you have a [GitHub account](https://github.com/signup/free). ++ Make sure you have a [GitHub account](https://github.com/signup). This is not essential, but makes providing patches much easier. + If you're planning to implement a new feature it makes sense to discuss your changes on the [dev list](https://commons.apache.org/mail-lists.html) first. This way you can make sure you're not wasting your time on something that isn't considered to be in Apache Commons Lang's scope. + Submit a [Jira Ticket][jira] for your issue, assuming one does not already exist. + Clearly describe the issue including steps to reproduce when it is a bug. + Make sure you fill in the earliest version that you know has the issue. + Find the corresponding [repository on GitHub](https://github.com/apache/?query=commons-), -[fork](https://help.github.com/articles/fork-a-repo/) and check out your forked repository. +[fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and check out your forked repository. If you don't have a GitHub account, you can still clone the Commons repository. Making Changes -------------- + Create a _topic branch_ for your isolated work. - * Usually you should base your branch on the `master` or `trunk` branch. - * A good topic branch name can be the JIRA bug id plus a keyword, e.g. `LANG-123-InputStream`. + * Usually you should base your branch from the `master` branch. + * A good topic branch name can be the JIRA bug ID plus a keyword, e.g. `LANG-123-InputStream`. * If you have submitted multiple JIRA issues, try to maintain separate branches and pull requests. + Make commits of logical units. * Make sure your commit messages are meaningful and in the proper format. Your commit message should contain the key of the JIRA issue. - * e.g. `LANG-123: Close input stream earlier` + * For example, `[LANG-123] Close input stream sooner` + Respect the original code style: - + Only use spaces for indentation. + + Only use spaces for indentation; you can check for unnecessary whitespace with `git diff` before committing. + Create minimal diffs - disable _On Save_ actions like _Reformat Source Code_ or _Organize Imports_. If you feel the source code should be reformatted create a separate PR for this change first. - + Check for unnecessary whitespace with `git diff` -- check before committing. -+ Make sure you have added the necessary tests for your changes, typically in `src/test/java`. -+ Run all the tests with `mvn clean verify` to assure nothing else was accidentally broken. ++ Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible but is a best practice. +Unit tests are typically in the `src/test/java` directory. ++ Run a successful build using the default [Maven](https://maven.apache.org/) goal with `mvn`; that's `mvn` on the command line by itself. ++ Write a pull request description that is detailed enough to understand what the pull request does, how, and why. ++ Each commit in the pull request should have a meaningful subject line and body. Note that commits might be squashed by a maintainer on merge. + Making Trivial Changes ---------------------- @@ -80,7 +82,7 @@ Making Trivial Changes The JIRA tickets are used to generate the changelog for the next release. For changes of a trivial nature to comments and documentation, it is not always necessary to create a new ticket in JIRA. -In this case, it is appropriate to start the first line of a commit with '(doc)' instead of a ticket number. +In this case, it is appropriate to start the first line of a commit with '[doc]' or '[javadoc]' instead of a ticket number. Submitting Changes @@ -106,10 +108,9 @@ Additional Resources + [Contributing patches](https://commons.apache.org/patches.html) + [Apache Commons Lang JIRA project page][jira] + [Contributor License Agreement][cla] -+ [General GitHub documentation](https://help.github.com/) -+ [GitHub pull request documentation](https://help.github.com/articles/creating-a-pull-request/) ++ [General GitHub documentation](https://docs.github.com/) ++ [GitHub pull request documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) -+ `#apache-commons` IRC channel on `irc.freenode.net` [cla]:https://www.apache.org/licenses/#clas [jira]:https://issues.apache.org/jira/browse/LANG diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 36799d0f5ff..00000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,115 +0,0 @@ -#!groovy - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -pipeline { - agent { - node { - label 'ubuntu' - } - } - - tools { - maven 'Maven 3 (latest)' - jdk 'JDK 1.8 (latest)' - } - - stages { - stage('Build') { - steps { - sh 'mvn' - } - post { - always { - junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true) - } - } - } - stage('Deploy') { - when { - branch 'master' - } - steps { - sh 'mvn deploy' - } - } - } - - // Send out notifications on unsuccessful builds. - post { - // If this build failed, send an email to the list. - failure { - script { - if(env.BRANCH_NAME == "master") { - def state = (currentBuild.previousBuild != null) && (currentBuild.previousBuild.result == 'FAILURE') ? "Still failing" : "Failure" - emailext( - subject: "[Lang] Change on branch \"${env.BRANCH_NAME}\": ${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - $state", - body: """The Apache Jenkins build system has built ${env.JOB_NAME} (build #${env.BUILD_NUMBER}) - -Status: ${currentBuild.result} - -Check console output at ${env.BUILD_URL} to view the results. -""", - to: "notifications@commons.apache.org", - recipientProviders: [[$class: 'DevelopersRecipientProvider']] - ) - } - } - } - - // If this build didn't fail, but there were failing tests, send an email to the list. - unstable { - script { - if(env.BRANCH_NAME == "master") { - def state = (currentBuild.previousBuild != null) && (currentBuild.previousBuild.result == 'UNSTABLE') ? "Still unstable" : "Unstable" - emailext( - subject: "[Lang] Change on branch \"${env.BRANCH_NAME}\": ${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - $state", - body: """The Apache Jenkins build system has built ${env.JOB_NAME} (build #${env.BUILD_NUMBER}) - -Status: ${currentBuild.result} - -Check console output at ${env.BUILD_URL} to view the results. -""", - to: "notifications@commons.apache.org", - recipientProviders: [[$class: 'DevelopersRecipientProvider']] - ) - } - } - } - - // Send an email, if the last build was not successful and this one is. - success { - script { - if ((env.BRANCH_NAME == "master") && (currentBuild.previousBuild != null) && (currentBuild.previousBuild.result != 'SUCCESS')) { - emailext ( - subject: "[Lang] Change on branch \"${env.BRANCH_NAME}\": ${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - Back to normal", - body: """The Apache Jenkins build system has built ${env.JOB_NAME} (build #${env.BUILD_NUMBER}) - -Status: ${currentBuild.result} - -Check console output at ${env.BUILD_URL} to view the results. -""", - to: "notifications@commons.apache.org", - recipientProviders: [[$class: 'DevelopersRecipientProvider']] - ) - } - } - } - } -} diff --git a/LICENSE.txt b/LICENSE.txt index d6456956733..ff9ad4530f5 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -193,7 +193,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/NOTICE.txt b/NOTICE.txt index 13a31408974..246f39cc1d1 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache Commons Lang -Copyright 2001-2019 The Apache Software Foundation +Copyright 2001-2026 The Apache Software Foundation This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). +The Apache Software Foundation (https://www.apache.org/). diff --git a/README.md b/README.md index ae9cdc2041c..116f814b13d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates | +======================================================================+ | | - | 1) Re-generate using: mvn commons:readme-md | + | 1) Re-generate using: mvn commons-build:readme-md | | | | 2) Set the following properties in the component's pom: | | - commons.componentid (required, alphabetic, lower case) | @@ -43,65 +43,85 @@ Apache Commons Lang =================== -[![Build Status](https://travis-ci.org/apache/commons-lang.svg)](https://travis-ci.org/apache/commons-lang) -[![Coverage Status](https://coveralls.io/repos/apache/commons-lang/badge.svg)](https://coveralls.io/r/apache/commons-lang) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.commons/commons-lang3/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.commons/commons-lang3/) -[![Javadocs](https://javadoc.io/badge/org.apache.commons/commons-lang3/3.9.svg)](https://javadoc.io/doc/org.apache.commons/commons-lang3/3.9) +[![Java CI](https://github.com/apache/commons-lang/actions/workflows/maven.yml/badge.svg)](https://github.com/apache/commons-lang/actions/workflows/maven.yml) +[![Maven Central](https://img.shields.io/maven-central/v/org.apache.commons/commons-lang3?label=Maven%20Central)](https://search.maven.org/artifact/org.apache.commons/commons-lang3) +[![Javadocs](https://javadoc.io/badge/org.apache.commons/commons-lang3/3.20.0.svg)](https://javadoc.io/doc/org.apache.commons/commons-lang3/3.20.0) +[![CodeQL](https://github.com/apache/commons-lang/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/apache/commons-lang/actions/workflows/codeql-analysis.yml) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/apache/commons-lang/badge)](https://api.securityscorecards.dev/projects/github.com/apache/commons-lang) Apache Commons Lang, a package of Java utility classes for the classes that are in java.lang's hierarchy, or are considered to be so standard as to justify existence in java.lang. + The code is tested using the latest revision of the JDK for supported + LTS releases: 8, 11, 17, 21 and 25 currently. + See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + + Please ensure your build environment is up-to-date and kindly report any build issues. + Documentation ------------- -More information can be found on the [Apache Commons Lang homepage](https://commons.apache.org/proper/commons-lang/). -The [Javadoc](https://commons.apache.org/proper/commons-lang/javadocs/api-release/) can be browsed. -Questions related to the usage of Apache Commons Lang should be posted to the [user mailing list][ml]. +More information can be found on the [Apache Commons Lang homepage](https://commons.apache.org/proper/commons-lang). +The [Javadoc](https://commons.apache.org/proper/commons-lang/apidocs) can be browsed. +Questions related to the usage of Apache Commons Lang should be posted to the [user mailing list](https://commons.apache.org/mail-lists.html). -Where can I get the latest release? ------------------------------------ -You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-lang3/download_lang3.cgi). +Getting the latest release +-------------------------- +You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-lang/download_lang.cgi). -Alternatively you can pull it from the central Maven repositories: +Alternatively, you can pull it from the central Maven repositories: ```xml org.apache.commons commons-lang3 - 3.9 + 3.20.0 ``` +Building +-------- + +Building requires a Java JDK and [Apache Maven](https://maven.apache.org/). +The required Java version is found in the `pom.xml` as the `maven.compiler.source` property. + +From a command shell, run `mvn` without arguments to invoke the default Maven goal to run all tests and checks. + Contributing ------------ -We accept Pull Requests via GitHub. The [developer mailing list][ml] is the main channel of communication for contributors. +We accept Pull Requests via GitHub. The [developer mailing list](https://commons.apache.org/mail-lists.html) is the main channel of communication for contributors. There are some guidelines which will make applying PRs easier for us: + No tabs! Please use spaces for indentation. -+ Respect the code style. ++ Respect the existing code style for each file. + Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change. -+ Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running ```mvn clean test```. ++ Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running `mvn`. ++ Before you push a PR, run `mvn` (without arguments). This runs the default goal which contains all build checks. ++ To see the code coverage report, regardless of coverage failures, run `mvn clean site -Dcommons.jacoco.haltOnFailure=false -Pjacoco` If you plan to contribute on a regular basis, please consider filing a [contributor license agreement](https://www.apache.org/licenses/#clas). You can learn more about contributing via GitHub in our [contribution guidelines](CONTRIBUTING.md). License ------- -This code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0). +This code is licensed under the [Apache License v2](https://www.apache.org/licenses/LICENSE-2.0). See the `NOTICE.txt` file for required notices and attributions. -Donations ---------- -You like Apache Commons Lang? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development. +Donating +-------- +You like Apache Commons Lang? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support development. Additional Resources -------------------- + [Apache Commons Homepage](https://commons.apache.org/) + [Apache Issue Tracker (JIRA)](https://issues.apache.org/jira/browse/LANG) ++ [Apache Commons Slack Channel](https://the-asf.slack.com/archives/C60NVB8AD) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) -+ `#apache-commons` IRC channel on `irc.freenode.org` -[ml]:https://commons.apache.org/mail-lists.html +Apache Commons Components +------------------------- + +Please see the [list of components](https://commons.apache.org/components.html) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index f9ee7aa22f3..c8b809c6cca 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,6 +1,1374 @@ - Apache Commons Lang - Version 3.9 - Release Notes + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Apache Commons Lang 3.20.0 Release Notes +---------------------------------------- + +The Apache Commons Lang team is pleased to announce the release of Apache Commons Lang 3.20.0. + +Commons Lang is a set of utility functions and reusable components that should be useful in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, using those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17, 21 and 25 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +This is a feature and maintenance release. Java 8 or later is required. + +Changes in this version include: + +New features: +o Add SystemProperties.getPath(String, Supplier). Thanks to Gary Gregory. +o Add JavaVersion.JAVA_25. Thanks to Gary Gregory. +o Add JavaVersion.JAVA_26. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_25. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_26. Thanks to Gary Gregory. +o Add MutablePair.ofNonNull(Map.Entry). Thanks to jack5505, Gary Gregory. +o Add TimedSemaphore.builder(), Builder, and deprecate constructors. Thanks to Gary Gregory. +o LANG-1504: Adding labels and history to split StopWatch #1473. Thanks to Edwin Delgado H, Gary Gregory. + +Fixed Bugs: +o Optimize ObjectToStringComparator.compare() method #1449. Thanks to mayuming, Gary Gregory. +o [javadoc] Improve StringUtils Javadoc #1450. Thanks to Marcono1234, Gary Gregory. +o Fix internal inverted logic in private isEnum() method and correct its usage in getFirstEnum() #1454. Thanks to mayuming, Gary Gregory. +o Use accessors in ToStringStyle so subclasses can effectively override them. Thanks to William Degrange, Gary Gregory, Rob Spoor. +o `LocaleUtils.toLocale(String)` for a 2 letter country code now returns a value instead of throwing an `IllegalArgumentException`. Thanks to jack5505, Gary Gregory. +o Fix typo in StringUtils.trunctate() IllegalArgumentException message and test assertion messages. Thanks to mayuming, Gary Gregory. +o Fix test fixture in ReflectionDiffBuilderTest.testTransientFieldDifference() #1464. Thanks to mayuming, Gary Gregory. +o LANG-1789: NullPointerException when generating NoSuchMethodException in MethodUtils. Thanks to Hylke van der Schaaf, Gary Gregory. +o LANG-1786: Map deprecated TimeZone short IDs and avoid JRE WARNINGs to the console #1483. Thanks to Daniel Migowski, Gary Gregory, Lenny Primak. +o LANG-1792: TypeUtils.toString() skips angle brackets for Class type. Thanks to Mykhailo Hryb, Gary Gregory. +o Mention JDK 25 LTS as a tested version in the release notes #1485. Thanks to Sebastian Peters, Gary Gregory. + +Changes: +o Bump org.apache.commons:commons-parent from 88 to 92 #1472, #1484. Thanks to Gary Gregory, Dependabot. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +Apache Commons Team + +----------------------------------------------------------------------------- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Apache Commons Lang 3.20.0 Release Notes +---------------------------------------- + +The Apache Commons Lang team is pleased to announce the release of Apache Commons Lang 3.20.0. + +Commons Lang is a set of utility functions and reusable components that should be useful in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, using those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17 and 21 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +This is a feature and maintenance release. Java 8 or later is required. + +Changes in this version include: + +New features: +o Add SystemProperties.getPath(String, Supplier). Thanks to Gary Gregory. +o Add JavaVersion.JAVA_25. Thanks to Gary Gregory. +o Add JavaVersion.JAVA_26. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_25. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_26. Thanks to Gary Gregory. +o Add MutablePair.ofNonNull(Map.Entry). Thanks to jack5505, Gary Gregory. +o Add TimedSemaphore.builder(), Builder, and deprecate constructors. Thanks to Gary Gregory. +o LANG-1504: Adding labels and history to split StopWatch #1473. Thanks to Edwin Delgado H, Gary Gregory. + +Fixed Bugs: +o Optimize ObjectToStringComparator.compare() method #1449. Thanks to mayuming, Gary Gregory. +o [javadoc] Improve StringUtils Javadoc #1450. Thanks to Marcono1234, Gary Gregory. +o Fix internal inverted logic in private isEnum() method and correct its usage in getFirstEnum() #1454. Thanks to mayuming, Gary Gregory. +o Use accessors in ToStringStyle so subclasses can effectively override them. Thanks to William Degrange, Gary Gregory, Rob Spoor. +o `LocaleUtils.toLocale(String)` for a 2 letter country code now returns a value instead of throwing an `IllegalArgumentException`. Thanks to jack5505, Gary Gregory. +o Fix typo in StringUtils.trunctate() IllegalArgumentException message and test assertion messages. Thanks to mayuming, Gary Gregory. +o Fix test fixture in ReflectionDiffBuilderTest.testTransientFieldDifference() #1464. Thanks to mayuming, Gary Gregory. +o LANG-1789: NullPointerException when generating NoSuchMethodException in MethodUtils. Thanks to Hylke van der Schaaf, Gary Gregory. +o LANG-1786: Map deprecated TimeZone short IDs and avoid JRE WARNINGs to the console #1483. Thanks to Daniel Migowski, Gary Gregory, Lenny Primak. + +Changes: +o Bump org.apache.commons:commons-parent from 88 to 92 #1472, #1484. Thanks to Gary Gregory, Dependabot. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.19.0 Release Notes +---------------------------------------- + +The Apache Commons Lang team is pleased to announce the release of Apache Commons Lang 3.19.0. + +Commons Lang is a set of utility functions and reusable components that should be useful in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, using those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17 and 21 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +This is a feature and maintenance release. Java 8 or later is required. + +Changes in this version include: + +New features: +o Add ArrayUtils.SOFT_MAX_ARRAY_LENGTH. Thanks to Gary Gregory. +o Add SystemUtils.IS_OS_NETWARE. Thanks to Gary Gregory. +o Add MethodUtils.getAccessibleMethod(Class, Method). Thanks to Gary Gregory. +o Add documentation to site for CVE-2025-48924 ClassUtils.getClass(...) can throw a StackOverflowError on very long inputs. Thanks to Gary Gregory. +o Add StringUtils.indexOfAny(CharSequence, int, char...). Thanks to Gary Gregory. +o Add ConcurrentException.ConcurrentException(String). Thanks to Gary Gregory. +o Add DateUtils.toLocalDateTime(Date[, TimeZone]) #1385. Thanks to Finger, Gary Gregory, Piotr P. Karwasz. +o Add DateUtils.toOffsetDateTime(Date[, TimeZone]). Thanks to Gary Gregory. +o Add DateUtils.toZonedDateTime(Date[, TimeZone]). Thanks to Gary Gregory. +o Add ByteConsumer. Thanks to Gary Gregory. +o Add ByteSupplier. Thanks to Gary Gregory. +o Add FailableByteConsumer. Thanks to Gary Gregory. +o Add FailableByteSupplier. Thanks to Gary Gregory. +o LANG-1784: Add Functions methods for null-safe mapping and chaining #1435. Thanks to Rich Dougherty, Gary Gregory. +o LANG-1784: Add Failable methods for null-safe mapping and chaining #1435. Thanks to Rich Dougherty, Gary Gregory. +o Add DoubleRange.fit(double). Thanks to Gary Gregory. +o Add IntegerRange.fit(int). Thanks to Gary Gregory. +o Add LongRange.fit(long). Thanks to Gary Gregory. +o Add DurationUtils.get(String, TemporalUnit, long). Thanks to Gary Gregory. +o Add DurationUtils.getMillis(String, long). Thanks to Gary Gregory. +o Add DurationUtils.getSeconds(String, long). Thanks to Gary Gregory. +o Add SystemProperties.getBoolean(Class, String, boolean). Thanks to Gary Gregory. +o Add SystemProperties.getInt(Class, String, int). Thanks to Gary Gregory. +o Add SystemProperties.getLong(Class, String, long). Thanks to Gary Gregory. + +Fixed Bugs: +o LANG-1778: MethodUtils.getMatchingMethod() doesn't respect the hierarchy of methods #1414. Thanks to wuwu2000. +o MethodUtils.getMethodObject(Class, String, Class...) now returns null instead of throwing a NullPointerException, as it does for other exception types. Thanks to Gary Gregory. +o Reduce spurious failures in ArrayUtilsTest methods that test ArrayUtils.shuffle() methods. Thanks to Gary Gregory. +o MethodUtils cannot find or invoke a public method on a public class implemented in its package-private superclass. Thanks to Gary Gregory. +o AtomicSafeInitializer.get() can spin internally if the FailableSupplier given to AbstractConcurrentInitializer.AbstractBuilder.setInitializer(FailableSupplier) throws a RuntimeException. Thanks to Stanislav Fort, Gary Gregory. +o LANG-1783: WordUtils.containsAllWords?() may throw PatternSyntaxException. Thanks to Arnout Engelen, Stanislav Fort, Gary Gregory. +o LANG-1782: MethodUtils cannot find or invoke vararg methods without providing vararg types or values #1427. Thanks to Joe Ferner, Gary Gregory. +o MethodUtils cannot find or invoke vararg methods of interface types. Thanks to Joe Ferner, Gary Gregory. +o MethodUtils cannot find or invoke vararg methods when widening primitive types following the JLS 5.1.2. Widening Primitive Conversion. Thanks to Joe Ferner, Gary Gregory. +o LANG-1597: Invocation fails because matching varargs method found but then discarded. Thanks to Richard Eckart de Castilho, Gary Gregory. +o Don't check accessibility twice in MemberUtils.setAccessibleWorkaround(T). Thanks to Gary Gregory. +o LANG-1774: Improve handling of ClassUtils.getShortCanonicalName() for invalid input #1437. Thanks to Zhongxin Yan, Madhur Lathi, Yudan Liu, Gary Gregory. +o LANG-1720: Improve Javadocs for Conversion. Thanks to Sheung Chi Chan, Arthur Chan, Gary Gregory, Elliotte Rusty Harold. +o Fix CalendarUtils.toLocalDate() Javadoc return type description #1440. Thanks to mayuming. +o Fix the method name in Javadoc examples for CharUtils.isHex() #1444. Thanks to mayuming. +o Deprecate NumberUtils.compare(byte, byte) in favor of Byte.compare(byte, byte). Thanks to Gary Gregory. +o Deprecate NumberUtils.compare(int, int) in favor of Integer.compare(int, int). Thanks to Gary Gregory. +o Deprecate NumberUtils.compare(long, long) in favor of Long.compare(long, long). Thanks to Gary Gregory. +o Deprecate NumberUtils.compare(short, short) in favor of Short.compare(short, short). Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.AWT_TOOLKIT. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_AWT_FONTS. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_AWT_GRAPHICSENV. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_AWT_HEADLESS. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_AWT_PRINTERJOB. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_COMPILER. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_ENDORSED_DIRS. Thanks to Gary Gregory. +o Deprecate obsolete system property constant SystemProperties.JAVA_EXT_DIRS. Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getAwtToolkit() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaAwtFonts() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaAwtGraphicsenv() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaAwtHeadless() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaAwtPrinterjob() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaCompiler() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaEndorsedDirs() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemProperties.getJavaExtDirs() Thanks to Gary Gregory. +o Deprecate method for obsolete system property constant SystemUtils.isJavaAwtHeadless() Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_FONTS. Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_GRAPHICSENV. Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_HEADLESS. Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_PRINTERJOB. Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_COMPILER. Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_ENDORSED_DIRS. Thanks to Gary Gregory. +o Deprecate constants for obsolete system property SystemUtils.JAVA_EXT_DIRS. Thanks to Gary Gregory. +o [javadoc] General improvements. Thanks to Gary Gregory. +o [javadoc] Fix thrown exception documentation for MethodUtils.getMethodObject(Class, String, Class...). Thanks to Gary Gregory. +o [javadoc] Strings::equalsAny: CI doc string should show it's insensitive #1416. Thanks to Scott Parish. +o [javadoc] General Javadoc improvements. Thanks to Gary Gregory. +o LANG-1780: [javadoc] Fix Strings Javadoc #1419. Thanks to tza. +o [javadoc] Fix typo in Javadoc of Strings instances #1406. Thanks to Sebastian Steiner. +o [javadoc] Fix Javadocs in ClassUtils #1410. Thanks to Hassan A Hashim. +o [javadoc] Fix @deprecated link for StringUtils#startsWithAny #1424. Thanks to mfg92. +o Replace old feather logotype with new oak logotype. Thanks to Gary Gregory. + +Changes: +o [test] Bump org.apache.commons:commons-text from 1.13.1 to 1.14.0. Thanks to Gary Gregory. +o Bump org.apache.commons:commons-parent from 85 to 88. Thanks to Gary Gregory. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.18.0 Release Notes +---------------------------------------- + +The Apache Commons Lang team is pleased to announce the release of Apache Commons Lang 3.18.0. + +Commons Lang is a set of utility functions and reusable components that should be useful in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, using those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17 and 21 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +This is a feature and maintenance release. Java 8 or later is required. + +Changes in this version include: + +New features: +o Add Strings and refactor StringUtils. Thanks to Gary Gregory. +o LANG-1747: Add StopWatch.run([Failable]Runnable) and get([Failable]Supplier). Thanks to Oliver B. Fischer, Gary Gregory. +o Add JavaVersion.JAVA_23. Thanks to Gary Gregory. +o Add JavaVersion.JAVA_24. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_23. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_24. Thanks to Gary Gregory. +o Add IntegerRange.toIntStream(). Thanks to Gary Gregory. +o Add LongRange.toLongStream(). Thanks to Gary Gregory. +o Add IntStrams.of(int...). Thanks to Gary Gregory. +o Add ArrayUtils.containsAny(int[], int...). Thanks to Gary Gregory. +o Add CalendarUtils.toLocalDate() #725. Thanks to asgh, Gary Gregory. +o Add SystemUtils.IS_OS_MAC_OSX_SEQUOIA. Thanks to Gary Gregory. +o Add BasicThreadFactory.builder() and deprecate BasicThreadFactory.Builder(). Thanks to Gary Gregory. +o Add BasicThreadFactory.daemon(). Thanks to Gary Gregory. +o Add ArrayUtils.startsWith. Thanks to Gary Gregory. +o Add Predicates. Thanks to Gary Gregory. +o Add RegExUtils methods typed to CharSequence input and deprecate old versions typed to String. Thanks to Gary Gregory. +o Add IterableStringTokenizer. Thanks to Gary Gregory. +o Add FailableIntToFloatFunction. Thanks to Gary Gregory. +o Add Validate.isTrue(boolean, Supplier). Thanks to Gary Gregory. +o Add EnumUtils.getFirstEnum(Class, int, ToIntFunction, E). Thanks to Gary Gregory. +o Add FailableToBooleanFunction. Thanks to Gary Gregory. +o Add the @FunctionalInterface annotation to org.apache.commons.lang3.concurrent.Computable. Thanks to Gary Gregory. +o Add SystemUtils.getJavaIoTmpDirPath(). Thanks to Gary Gregory. +o Add SystemUtils.getJavaHomePath(). Thanks to Gary Gregory. +o Add SystemUtils.getUserDirPath(). Thanks to Gary Gregory. +o Add SystemUtils.getUserHomePath(). Thanks to Gary Gregory. +o Add ArrayFill.fill(T[], FailableIntFunction)). Thanks to Gary Gregory. +o Add SystemProperties.JAVA_SECURITY_DEBUG. Thanks to Gary Gregory. +o Add SystemProperties.JAVA_SECURITY_KERBEROS_CONF. Thanks to Gary Gregory. +o Add SystemProperties.JAVA_SECURITY_KERBEROS_KDC. Thanks to Gary Gregory. +o Add SystemProperties.JAVA_SECURITY_KERBEROS_REAL. Thanks to Gary Gregory. +o Add ArrayFill.fill(boolean[], boolean) #1386. Thanks to kommalapatiraviteja. +o Add ObjectUtils.getIfNull(Object, Object) and deprecate defaultIfNull(Object, Object). Thanks to Pankraz76, Gary Gregory. +o org.apache.commons.lang3.mutable.Mutable now extends Supplier. Thanks to Gary Gregory. +o Add org.apache.commons.lang3.CharUtils.isHex(char). Thanks to Gary Gregory. +o Add org.apache.commons.lang3.CharUtils.isOctal(char). Thanks to Gary Gregory. +o Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.reentrantLockVisitor(Object). Thanks to Gary Gregory. +o Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.create(Object, ReentrantLock). Thanks to Gary Gregory. +o Add org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReentrantLockVisitor. Thanks to Gary Gregory. +o Add builders for LockingVisitors implementations. Thanks to Gary Gregory. +o Add EnumSet.stream(Class). Thanks to Gary Gregory. +o Add org.apache.commons.lang3.SystemProperties.isPropertySet(String). Thanks to Gary Gregory. + +Fixed Bugs: +o Fix flaky FileUtilsWaitForTest.testWaitForNegativeDuration(). Thanks to Gary Gregory. +o Pick up exec-maven-plugin version from parent POM. Thanks to Gary Gregory. +o Speed up and sanitize StopWatchTest. Thanks to Gary Gregory. +o Fix handling of non-ASCII letters and numbers in RandomStringUtils #1273. Thanks to Fabrice Benhamouda. +o Rewrite ClassUtils.getClass(...) without recursion to avoid StackOverflowError on very long inputs. + OSS-Fuzz Issue 42522972: apache-commons-text:StringSubstitutorInterpolatorFuzzer: Security exception in org.apache.commons.lang3.ClassUtils.getClass. Thanks to OSS-Fuzz, Gary Gregory. +o Remove trailing whitespace in StopWatch exception messages. Thanks to Gary Gregory. +o LANG-1754: Use getAllSuperclassesAndInterfaces() in getMatchingMethod() #1289. Thanks to vhbcm. +o Add details to the ArrayFill Javadoc. Thanks to Gary Gregory. +o Add details to the ArraySorter Javadoc. Thanks to Gary Gregory. +o Fix broken URL to project location in Maven Central #1296. Thanks to Capt. Cutlass. +o LANG-1753: StringUtils.replaceEachRepeatedly regression in 3.11+ #1297. Thanks to Capt. Cutlass. +o Use simplified JUnit assertion methods #1298. Thanks to Capt. Cutlass. +o LANG-1682: Javadoc and test: Use Strings.CI.startsWithAny method instead #1299. Thanks to Capt. Cutlass. +o Fix NullPointerException in FastDateParser.TimeZoneStrategy.setCalendar(FastDateParser, Calendar, String) on Java 23. Thanks to Gary Gregory. +o LANG-1757: Fix NullPointerException in MethodUtils.getMatchingAccessibleMethod((Class, String, Class...)). Thanks to Gary Gregory. +o LANG-1698: Fix StackOverflowError in TypeUtils.typeVariableToString(TypeVariable), TypeUtils.toString(Type) on Java 17 and up. Thanks to Jan Arne Sparka, Gary Gregory. +o LANG-1511: SystemUtils is missing important documentation. Thanks to david cogen, Gary Gregory, Bruno P. Kinoshita. +o Make Failable.run(FailableRunnable) null-safe. Thanks to Gary Gregory. +o Make Failable.accept(*) null-safe. Thanks to Gary Gregory. +o Improve container detection by mimicking systemd #1323. Thanks to maxxedev, Piotr P. Karwasz, Gary Gregory. +o Make LangCollectors.collect(...) null-safe. Thanks to Gary Gregory. +o Make LangCollectors.collect(...) null-safe. Thanks to Gary Gregory. +o Fix names of UTF-16 surrogate character test fixture constants, see also #1326. Thanks to IBue, Gary Gregory. +o Moditect plugin generates split package warnings. Thanks to Gary Gregory. +o LocaleUtils.availableLocaleSet() uses predictable iteration order. Thanks to Gary Gregory. +o LANG-1759: SerializationUtils.clone(Object) throws ClassCastException when called with a Serializable lambda. Thanks to Maxim Butov, Gary Gregory. +o LANG-1759: [StringUtils::indexOfAnyBut] redesign due to inconsistent/faulty behavior regarding UTF-16 surrogates #1327. Thanks to IBue, Gary Gregory, Piotr P. Karwasz. +o Undeprecate ObjectUtils.toString(Object). Thanks to Gary Gregory. +o Fix Spotbugs [ERROR] Medium: The field org.apache.commons.lang3.builder.DiffBuilder$SDiff.leftSupplier is transient but isn't set by deserialization [org.apache.commons.lang3.builder.DiffBuilder$SDiff] In DiffBuilder.java SE_TRANSIENT_FIELD_NOT_RESTORED. Thanks to Gary Gregory. +o Fix Spotbugs [ERROR] Medium: The field org.apache.commons.lang3.builder.DiffBuilder$SDiff.rightSupplier is transient but isn't set by deserialization [org.apache.commons.lang3.builder.DiffBuilder$SDiff] In DiffBuilder.java SE_TRANSIENT_FIELD_NOT_RESTORED. Thanks to Gary Gregory. +o LANG-1762: StopWatch methods should not delegate to deprecated methods. Thanks to Alonso Gonzalez, Gary Gregory. +o Don't call TypeUtils.toString(Type) on every array item in TypeUtils.parameterize[WithOwner](Type, Class, Map, Type>) unless required. Thanks to Gary Gregory. +o Remove -nouses directive from maven-bundle-plugin. OSGi package imports now state 'uses' definitions for package imports, this doesn't affect JPMS (from org.apache.commons:commons-parent:80). Thanks to Gary Gregory. +o Instead of throwing a NullPointerException, ArrayUtils.toStringArray(Object[]) should return "null" for null elements like ArrayUtils.toStringArray(Object[], String) returns its valueForNullElements. Thanks to Gary Gregory. +o LANG-1764: Deprecate NumericEntityUnescaper.OPTION in favor of Apache Commons Text. Thanks to Gary Gregory. +o Several hash collisions in Fraction class. Thanks to Gary Gregory. +o LANG-1768: MutableLong and friends should provide better parsing exceptions Javadocs. Thanks to Wang Hailong, Gary Gregory. +o Reimplement StringUtils.toCodePoints(CharSequence) to use java.lang.CharSequence.codePoints(). Thanks to Gary Gregory. +o Reimplement StringUtils.capitalize(String) to use java.lang.CharSequence.codePoints(). Thanks to Gary Gregory. +o Reimplement StringUtils.uncapitalize(String) to use java.lang.CharSequence.codePoints(). Thanks to Gary Gregory. +o org.apache.commons.lang3.ClassUtils.getCanonicalName(String) now throws an IllegalArgumentException for array dimensions greater than 255. Thanks to Gary Gregory. +o Fix Javadoc typo and improve clarity in defaultIfBlank method #1376. Thanks to Sridhar Balijepalli, Piotr P. Karwasz. +o LANG-1773: Apache Commons Lang no longer builds on Android #1381. Thanks to Éamonn McManus, Gary Gregory. +o LANG-1772: Restrict size of cache to prevent overflow errors #1379. Thanks to James Winters, Piotr P. Karwasz, Gary Gregory. +o LANG-1772: Reimplement org.apache.commons.lang3.ClassUtils.hierarchy(Class, Interfaces) using an AtomicReference. Thanks to Gary Gregory. +o Fix Javadoc code examples in DiffBuilder and ReflectionDiffBuilder #1400. Thanks to Ken Dombeck. +o Fix generics in org.apache.commons.lang3.stream.Streams.toArray(Class) signature. Thanks to Gary Gregory. +o LANG-1727: EventListenerSupport doesn't document ordering of events. Thanks to Elliotte Rusty Harold, Gary Gregory. +o Fix edge-case NullPointerException in org.apache.commons.lang3.SystemUtils.IS_OS_ANDROID. Thanks to Gary Gregory. +o Fix edge-case NullPointerException in org.apache.commons.lang3.SystemUtils.isJavaVersionAtLeast(JavaVersion). Thanks to Gary Gregory. +o Fix edge-case NullPointerException in org.apache.commons.lang3.SystemUtils.isJavaVersionAtMost(JavaVersion). Thanks to Gary Gregory. +o Return the default enum if a SecurityException is caught in getEnumSystemProperty(). Thanks to Gary Gregory. +o Fix edge-case NullPointerException in org.apache.commons.lang3.EnumUtils.getEnum(Class, String, E). Thanks to Gary Gregory. +o org.apache.commons.lang3.EnumUtils.getFirstEnumIgnoreCase(Class, String, Function, E) now returns the given default enum on null enumClass input. Thanks to Gary Gregory. +o org.apache.commons.lang3.EnumUtils.getEnumIgnoreCase(Class, String, E) now returns the given default enum on null enumClass input. Thanks to Gary Gregory. +o org.apache.commons.lang3.EnumUtils.getEnumIgnoreCase(Class, String) now returns the given default enum on null enumClass input. Thanks to Gary Gregory. +o Fix NullPointerException in org.apache.commons.lang3.compare.ComparableUtils.ComparableCheckBuilder.equalTo(A). Thanks to Gary Gregory. +o Fix NullPointerException in org.apache.commons.lang3.compare.ComparableUtils.ComparableCheckBuilder.greaterThan(A). Thanks to Gary Gregory. +o Fix NullPointerException in org.apache.commons.lang3.compare.ComparableUtils.ComparableCheckBuilder.greaterThanOrEqualTo(A). Thanks to Gary Gregory. +o Fix NullPointerException in org.apache.commons.lang3.compare.ComparableUtils.ComparableCheckBuilder.lessThan(A). Thanks to Gary Gregory. +o Fix NullPointerException in org.apache.commons.lang3.compare.ComparableUtils.ComparableCheckBuilder.lessThanOrEqualTo(A). Thanks to Gary Gregory. +o LANG-1776: Use GitHub URL in POM for improved automation support. + +Changes: +o Bump org.apache.commons:commons-parent from 73 to 85 #1267, #1277, #1283, #1288, #1302, #1377. Thanks to Gary Gregory, Dependabot. +o [site] Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #1300. Thanks to Gary Gregory, Dependabot. +o [test] Bump org.easymock:easymock from 5.4.0 to 5.6.0 #1317, #1387. Thanks to Gary Gregory, Dependabot. +o [test] Bump org.apache.commons:commons-text from 1.12.0 to 1.13.1 #1336. Thanks to Gary Gregory, Dependabot. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.17.0 Release Notes +---------------------------------------- + +The Apache Commons team is pleased to announce Apache Commons Lang Version 3.17.0. + +Commons Lang is a set of utility functions and reusable components that should be useful in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, using those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17 and 21 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +This is a feature and maintenance release. Java 8 or later is required. + +Changes in this version include: + +New features: +o RandomUtils.secure() now uses SecureRandom() instead of SecureRandom.getInstanceStrong(). Thanks to Gary Gregory. +o RandomStringUtils.secure() now uses SecureRandom() instead of SecureRandom.getInstanceStrong(). Thanks to Gary Gregory. +o Remove unused exception from deprecated StringUtils.toString(byte[], String). Thanks to Gary Gregory. +o Make RandomUtils.insecure() public. Thanks to Gary Gregory. +o Add RandomUtils.secureStrong(). Thanks to Gary Gregory. +o Add RandomStringUtils.secureStrong(). Thanks to Gary Gregory. +o Add CalendarUtils.toLocalDateTime(Calendar). Thanks to Gary Gregory. +o Add CalendarUtils.toLocalDateTime(). Thanks to Gary Gregory. +o Add CalendarUtils.toZonedDateTime(Calendar). Thanks to Gary Gregory. +o Add CalendarUtils.toZonedDateTime(). Thanks to Gary Gregory. +o Add CalendarUtils.toOffsetDateTime(Calendar). Thanks to Gary Gregory. +o Add CalendarUtils.toOffsetDateTime(). Thanks to Gary Gregory. + +Fixed Bugs: +o LANG-1760: Using RandomStringUtils.insecure() still leads to using the secure() random. Thanks to Marco Hoek, Gary Gregory. +o Deprecate static RandomUtils.next*() methods in favor or .secure() and .insecure() versions. Thanks to Gary Gregory. +o Deprecate static RandomStringUtils.random*() methods in favor or .secure() and .insecure() versions. Thanks to Gary Gregory. + +Changes: +o Bump org.hamcrest:hamcrest from 2.2 to 3.0 #1255. Thanks to Gary Gregory, Dependabot. +o Bump org.easymock:easymock from 5.3.0 to 5.4.0 #1256. Thanks to Gary Gregory, Dependabot. +o Bump org.codehaus.mojo:exec-maven-plugin from 3.3.0 to 3.4.1 #1262, #1264. Thanks to Gary Gregory, Dependabot. +o Bump org.apache.commons:commons-parent from 72 to 73 #1265. Thanks to Gary Gregory, Dependabot. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.16.0 Release Notes +---------------------------------------- + +The Apache Commons team is pleased to announce Apache Commons Lang Version 3.16.0. + +Commons Lang is a set of utility functions and reusable components that should be useful in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, using those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17 and 21 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +This is a feature and maintenance release. Java 8 or later is required. + +Changes in this version include: + +New features: +o Add StopWatch.getSplitDuration() and deprecate getSplitTime(). Thanks to Gary Gregory. +o Add StopWatch.getStartInstant() and deprecate getStartTime(). Thanks to Gary Gregory. +o Add StopWatch.getStopInstant() and deprecate getStopTime(). Thanks to Gary Gregory. +o Add StopWatch.getDuration() and deprecate getTime(). Thanks to Gary Gregory. +o Add Javadoc links from StopWatch to DurationUtils #1249. Thanks to Oliver B. Fischer, Gary Gregory. +o Add LangCollectors.collect(Collector, T...). Thanks to Gary Gregory. +o Add RandomStringUtils.secure(). Thanks to Gary Gregory. +o Add RandomStringUtils.insecure(). Thanks to Gary Gregory. + +Fixed Bugs: +o Reimplement StopWatch internals to use java.time. Thanks to Gary Gregory. +o LANG-1745: RandomStringUtils.random() with a negative character index should throw IllegalArgumentException. Thanks to Wang Hailong, Gary Gregory. +o LANG-1741: LocaleUtils.toLocale(String) cannot parse four segments. Thanks to Wang Hailong, Gary Gregory. +o Use fewer intermediary strings in DefaultExceptionContext.getFormattedExceptionMessage(String). Thanks to Gary Gregory. +o Fix Javadoc in StringUtils.splitPreserveAllTokens() #1251. Thanks to VīŋŊclav Haisman. +o Deprecate ArraySort constructor for removal. Thanks to Gary Gregory. +o Deprecate CharEncoding constructor for removal. Thanks to Gary Gregory. +o Deprecate Conversion constructor for removal. Thanks to Gary Gregory. +o Deprecate Conversion constructor for removal. Thanks to Gary Gregory. +o Deprecate EntityArrays constructor for removal. Thanks to Gary Gregory. +o Deprecate ObjectToStringComparator constructor for removal. Thanks to Gary Gregory. +o Deprecate RuntimeEnvironment constructor for removal. Thanks to Gary Gregory. + +Changes: +o Bump org.apache.commons:commons-parent from 71 to 72 #1253. Thanks to Gary Gregory, Dependabot. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.15.0 Release Notes +---------------------------------------- + +The Apache Commons team is pleased to announce Apache Commons Lang Version 3.15.0. + +Commons Lang is a set of utility functions and reusable components that should be of use in any Java environment. + +Starting with Commons Lang 3.9, we target Java 8, making use of those features. + +For advice on upgrading from 2.x to 3.x, see: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +The code is tested using the latest revision of the JDK for supported +LTS releases: 8, 11, 17 and 21 currently. +See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + +Please ensure your build environment is up-to-date and kindly report any build issues. + +New features and bug fixes (Java 8 or above). + +Changes in this version include: + +New features: +o LANG-1724: Customize text pattern in DiffResult#toString(). Thanks to Gary Gregory, Dennis Baerten. +o Add DiffBuilder.Builder. Thanks to Gary Gregory. +o Add DiffBuilder.builder(). Thanks to Gary Gregory. +o Add ReflectionDiffBuilder.Builder. Thanks to Gary Gregory. +o Add ReflectionDiffBuilder.builder(). Thanks to Gary Gregory. +o Add test in TypeUtilsTest #1151. Thanks to Elliotte Rusty Harold. +o Add Streams.failableStream(T), non-varargs variant. Thanks to Gary Gregory. +o Add Streams.nonNull(T), non-varargs variant. Thanks to Gary Gregory. +o Add ArrayUtils.nullTo(T[], T[]). Thanks to Gary Gregory. +o Add T ArrayUtils.arraycopy(T, int, T, int, int) fluent style. Thanks to Gary Gregory. +o Add T ArrayUtils.arraycopy(T, int, int, int, Function) fluent style. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_22. Thanks to Gary Gregory. +o Add JavaVersion.JAVA_22. Thanks to Gary Gregory. +o Add SystemProperties.getUserName(Supplier). Thanks to Gary Gregory. +o Add SystemProperties.getLineSeparator(Supplier). Thanks to Gary Gregory. +o Add SystemProperties.getJavaSpecificationVersion(Supplier). Thanks to Gary Gregory. +o Add SystemProperties constants and methods for system properties as of Java 22. Thanks to Gary Gregory. +o Add MethodUtils.getMethodObject(Class, String, Class...). Thanks to Gary Gregory. +o LANG-1733: Add null-safe Consumers.accept() and Functions.apply() #1215. Thanks to Jongjin Bae, Gary Gregory. +o Add SystemUtils.IS_OS_ANDROID. Thanks to Gary Gregory. +o Add SystemUtils.IS_OS_MAC_OSX_SONOMA. Thanks to Gary Gregory. +o Add RuntimeEnvironment.inContainer() #1241. Thanks to Gary Gregory. +o Add AppendableJoiner and refactor string joining #1244. Thanks to Gary Gregory. + +Fixed Bugs: +o Improve Javadoc in ExceptionUtils #1136. Thanks to MiklīŋŊs KarakīŋŊ, Gary Gregory. +o Fixed two non-deterministic tests in EnumUtilsTest.java #1131. Thanks to Saiharshith Karuneegar Ramesh, Gary Gregory. +o LANG-1721: Fix wrong number check that cause StringIndexOutOfBoundsException #1140. Thanks to Arthur Chan, Gary Gregory. +o LANG-1722: Rethrow NegativeArraySizeException as SerializationException in SerializationUtils.deserialize(InputStream) #1141. Thanks to Arthur Chan. +o LANG-1723: Throw NumberFormatException instead of IndexOutOfBoundsException in NumberUtils.getMantissa(String, int) #1145. Thanks to Arthur Chan, Gary Gregory. +o Minor grammar fixes #1143. Thanks to ParanoīŋŊd User. +o LANG-1713: ArrayUtils will return null when adding two null arrays, but undocumented. Thanks to John Hendrikx, Gary Gregory. +o Let parent POM figure out commons.spdx.version. Thanks to Gary Gregory. +o LANG-1726: Undeprecate ExceptionUtils.rethrow(Throwable). Thanks to VīŋŊclav Haisman, Gary Gregory. +o LANG-1702: Test the Conversion class #1155. Thanks to Elliotte Rusty Harold. +o Address minor redundancies after code inspection #1148. Thanks to ParanoidUser, Elliotte Rusty Harold, Gary Gregory. +o Allow EventListenerSupport to handle (and ignore) exception from listeners allowing invocation of all listeners #1167. Thanks to Gary Gregory. +o Deprecate AnnotationUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ArchUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ArrayUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate BooleanUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate CharSequenceUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate CharSetUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate CharUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ClassLoaderUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ClassPathUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ClassUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ConstructorUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate DateFormatUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate DateUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate Diff.getType(). Thanks to Gary Gregory. +o Deprecate DiffBuilder.DiffBuilder(T, T, ToStringStyle). Thanks to Gary Gregory. +o Deprecate DiffBuilder.DiffBuilder(T, T, ToStringStyle, boolean). Thanks to Gary Gregory. +o Deprecate DurationFormatUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate DurationUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate EnumUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate EventUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate FieldUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate IEEE754rUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate InheritanceUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate IntStreams 0-argument constructor. Thanks to Gary Gregory. +o Deprecate LocaleUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate LockingVisitors 0-argument constructor. Thanks to Gary Gregory. +o Deprecate MemberUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate MethodUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate NumberUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ObjectUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate RandomStringUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate RandomUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ReflectionDiffBuilder.ReflectionDiffBuilder(T, T, ToStringStyle). Thanks to Gary Gregory. +o Deprecate RegExUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate SerializationUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate Streams 0-argument constructor. Thanks to Gary Gregory. +o Deprecate StringEscapeUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate StringUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate Suppliers 0-argument constructor. Thanks to Gary Gregory. +o Deprecate SystemProperties 0-argument constructor. Thanks to Gary Gregory. +o Deprecate ThreadUtils 0-argument constructor. Thanks to Gary Gregory. +o Deprecate TypeUtils 0-argument constructor. Thanks to Gary Gregory. +o Make ArrayFill null-safe. Thanks to Gary Gregory. +o Make ArraySorter null-safe. Thanks to Gary Gregory. +o Make ArrayUtils.removeAll() null-safe. Thanks to Gary Gregory. +o Fix Java version in README.md #1170. Thanks to Philipp Trulson, Gary Gregory. +o StringUtils.stripAccents() should handle ligatures, UTF32 math blocks, etc. #1201. Thanks to Stephan Peters, Gary Gregory, Bernd. +o LANG-1524: TypeUtils.toString(Type) StackOverflowError for an inner class in the inner class parameterized enclosing class #657. Thanks to kijong.youn, Aakash Gupta, Gary Gregory. +o Deprecate SystemUtils.getUserName(String) in favor of SystemProperties.getUserName(Supplier). Thanks to Gary Gregory. +o Make LockVisitor.acceptReadLocked(FailableConsumer) null-safe. Thanks to Gary Gregory. +o Make LockVisitor.applyWriteLocked(FailableConsumer) null-safe. Thanks to Gary Gregory. +o Make ObjectUtils.getFirstNonNull(Supplier...) null-safe. Thanks to Gary Gregory. +o Make SystemProperties.getLineSeparator(Supplier). Thanks to Gary Gregory. +o StringUtils.stripAccents(String) doesn't handle "\u0111" and "\u0110" (Vietnamese) #1216. Thanks to hunghhdev. +o StringUtils.stripAccents(String) doesn't handle I with bar. Thanks to Gary Gregory. +o StringUtils.stripAccents(String) doesn't handle U with bar. Thanks to Gary Gregory. +o StringUtils.stripAccents(String) doesn't handle T with stroke. Thanks to Gary Gregory. +o LANG-1735: Fix Javadoc for FluentBitSet.setInclusive(int, int) #1222. Thanks to Tobias Kiecker. +o Same Javadoc changes as [TEXT-234] #1223. Thanks to Tobias Kiecker. +o Remove duplicate static data in SerializationUtils.ClassLoaderAwareObjectInputStream. Thanks to Gary Gregory. +o Reimplement RandomUtils and RandomStringUtils on top of SecureRandom#getInstanceStrong() #1235. Thanks to Gary Gregory, Henri Yandell, Fabrice Benhamouda. +o LANG-1657: DiffBuilder: Type constraint for method append(..., DiffResult) too strict #786. Thanks to Matthias Welz, Andrew Thomas, Gary Gregory. + +Changes: +o Bump commons-parent from 64 to 71 #1194, #1233. Thanks to Dependabot, Gary Gregory. +o Bump org.codehaus.mojo:exec-maven-plugin from 3.1.1 to 3.3.0 #1175, #1224. Thanks to Dependabot. +o Bump org.apache.commons:commons-text from 1.11.0 to 1.12.0 #1200. Thanks to Dependabot. +o Bump org.easymock:easymock from 5.2.0 to 5.3.0 #1232. Thanks to Dependabot. +o Bump org.codehaus.mojo:taglist-maven-plugin from 3.0.0 to 3.1.0 #1242. Thanks to Dependabot. + +Removed: +o Drop obsolete JDK 13 Maven profile #1142. Thanks to ParanoīŋŊd User. + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +-Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.14.0 Release Notes +---------------------------------------- + +This document contains the release notes for the 3.14.0 version of Apache Commons Lang. +Commons Lang is a set of utility functions and reusable components that should be of use in any +Java environment. + +Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8. + +For the advice on upgrading from 2.x to 3.x, see the following page: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +New features and bug fixes (Java 8 or above). + +Changes in this version include: + +New features: +o Add Functions#function(Function). Thanks to Rob Spoor, Gary Gregory. +o Add FailableFunction#function(FailableFunction). Thanks to Rob Spoor, Gary Gregory. +o Add CalendarUtils.getInstance(). Thanks to Gary Gregory. +o Add syntax for optional tokens to DurationFormatUtils #1062. Thanks to Dan Watson. +o Add ArrayFill. Thanks to Gary Gregory. +o Add FastDateParser.TimeZoneStrategy.TzInfo.toString(). Thanks to Gary Gregory. +o Add LocaleUtils.isLanguageUndetermined(Locale). Thanks to Gary Gregory. +o Add ObjectUtils.toString(Supplier, Supplier). Thanks to Gary Gregory. +o Add LazyInitializer.isInitialized(). Thanks to Gary Gregory. +o Add ConcurrentInitializer#isInitialized() #1120. Thanks to Benjamin Confino, Gary Gregory. +o Add Streams.failableStream(T...). Thanks to Gary Gregory. +o Add FailableSupplier.nul(). Thanks to Gary Gregory. +o Add Suppliers.nul(). Thanks to Gary Gregory. +o Add ExceptionUtils.throwUnchecked(T) where T extends Throwable, and deprecate Object version. Thanks to Gary Gregory. +o Add ExceptionUtils.rethrowRuntimeException(T), and deprecate rethrow(T). Thanks to Gary Gregory. +o LANG-1716: ConcurrentInitializer implementations can now be instantiated and configured with allocation and release lambdas. Thanks to Benjamin Confino, Gary Gregory. +o LANG-1717: Add support for RISC-V in ArchUtils #1128. Thanks to Levi Zim, Gary Gregory. + +Fixed Bugs: +o Rename variable names from 'clss' to 'clazz' #1087. Thanks to remeio. +o [Javadoc] ComparableUtils'c1' to 'comparable1', 'c2' to ' Thanks to remeio. +o [Javadoc] Remove 2.1 specific comment #1091. Thanks to Elliotte Rusty Harold. +o LANG-1704: ImmutablePair and ImmutableTriple implementation don't match final in Javadoc. Thanks to Dan Ziemba, Gilles Sadowski, Alex Herbert, Gary Gregory. +o [Javadoc] Fix Incorrect Description in Processor isAarch64() #1093. Thanks to Sung Ho Yoon. +o [Javadoc] Point to right getShortClassName flavor in Javadoc for relevant notes #1097. Thanks to ljacqu. +o Improve performance of StringUtils.isMixedCase() #1096. Thanks to hduelme. +o LANG-1706: ThreadUtils find methods should not return null items #1098. Thanks to Alberto FernīŋŊndez. +o LANG-1710: ReflectionToStringBuilder changes in version 3.13.0 has broken the logic for overriding classes. Thanks to Shashank Sharma, Gary Gregory, Oksana. +o Return "null" instead of NPE in ClassLoaderUtils.toString(ClassLoader). Thanks to Gary Gregory. +o Return "null" instead of NPE in ClassLoaderUtils.toString(URLClassLoader). Thanks to Gary Gregory. +o Return ToStringStyle.nullText instead of NPE for ReflectionToStringBuilder.toString(). Thanks to Gary Gregory. +o Fix ThresholdCircuitBreaker#checkState() #1100. Thanks to yichinzhu, Gary Gregory. +o Use ConcurrentInitializer implementations without subclassing. #1123. Thanks to Benjamin Confino, Gary Gregory. +o Update critical value for chi-square test #1125. Thanks to Alex Herbert. +o Fix Javadoc syntax errors #1129. Thanks to Sung Ho Yoon. + +Changes: +o Bump commons-parent from 58 to 64. Thanks to Gary Gregory. +o Bump org.easymock:easymock from 5.1.0 to 5.2.0 #1104. Thanks to Gary Gregory. +o Bump commons-text from 1.10.0 to 1.11.0. Thanks to Gary Gregory. +o Bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.1.1 #1135. Thanks to Gary Gregory. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +-Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.13.0 Release Notes +---------------------------------------- + + +This document contains the release notes for the 3.13.0 version of Apache Commons Lang. +Commons Lang is a set of utility functions and reusable components that should be of use in any +Java environment. + +Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8. + +For the advice on upgrading from 2.x to 3.x, see the following page: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +New features and bug fixes (Java 8 or above). + +Changes in this version include: + +New features: +o Add GitHub coverage.yml. Thanks to Gary Gregory. +o Add EnumUtils.getEnumSystemProperty(...). Thanks to Gary Gregory. +o Add TriConsumer. Thanks to Gary Gregory. +o Add and use EnumUtils.getFirstEnumIgnoreCase(Class, String, Function, E). Thanks to Gary Gregory. +o Add and use Suppliers. Thanks to Gary Gregory. +o Add and use ArrayUtils.getComponentType(T[]). Thanks to Gary Gregory. +o Add and use ClassUtils.getComponentType(Class>T[]>). Thanks to Gary Gregory. +o Add and use ObjectUtils.getClass(T). Thanks to Gary Gregory. +o Add and use ArrayUtils.newInstance(Class>T>, int). Thanks to Gary Gregory. +o Add and use null-safe Streams.of(T...). Thanks to Gary Gregory. +o Add ClassUtils.comparator(). Thanks to Gary Gregory. +o Add and use ThreadUtils.sleepQuietly(Duration). Thanks to Gary Gregory. +o Add and use ArrayUtils.setAll(T[], IntFunction). Thanks to Gary Gregory. +o Add and use ArrayUtils.setAll(T[], Supplier). Thanks to Gary Gregory. +o Add BooleanConsumer. Thanks to Gary Gregory. +o Add IntToCharFunction. Thanks to Gary Gregory. +o Add IntStreams. Thanks to Gary Gregory. +o Add UncheckedFuture. Thanks to Gary Gregory. +o Add UncheckedException. Thanks to Gary Gregory. +o Add UncheckedExecutionException. Thanks to Gary Gregory. +o Add UncheckedTimeoutException. Thanks to Gary Gregory. +o Add UncheckedInterruptedException. Thanks to Gary Gregory. +o Add TimeZones.GMT. Thanks to Gary Gregory. +o Add ObjectUtils.identityHashCodeHex(Object). Thanks to Gary Gregory. +o Add ObjectUtils.hashCodeHex(Object). Thanks to Gary Gregory. +o Add StringUtils.removeStart(String, char). Thanks to Gary Gregory. +o LANG-1659: Add null-safe ObjectUtils.isArray() #754. Thanks to Arturo Bernal, Gary Gregory. +o Add ComparableUtils.max(A, A) and ComparableUtils.min(A, A). Thanks to Gary Gregory. +o Add UncheckedReflectiveOperationException. Thanks to Gary Gregory. +o Add and use ClassUtils.isPublic(Class). Thanks to Gary Gregory. +o Add UncheckedIllegalAccessException. Thanks to Gary Gregory. +o Add MethodInvokers. Thanks to Gary Gregory. +o Add Streams.nullSafeStream(Collection). Thanks to Gary Gregory. +o Add Streams.toStream(Collection). Thanks to Gary Gregory. +o Add Streams.failableStream(Collection) and deprecate misnamed stream(Collection). Thanks to Gary Gregory. +o Add Streams.failableStream(Stream) and deprecate misnamed stream(Stream). Thanks to Gary Gregory. +o Add EnumUtils.getEnumMap(Class, Function). #730 Thanks to Maxwell Cody, Gary Gregory. +o Add FluentBitSet. Thanks to Gary Gregory. +o Add Streams.instancesOf(Class, Collection). Thanks to Gary Gregory. +o Add ImmutablePair.ofNonNull(L, R). Thanks to Gary Gregory. +o Add ImmutableTriple.ofNonNull(L, M, R). Thanks to Gary Gregory. +o Add MutablePair.ofNonNull(L, R). Thanks to Gary Gregory. +o Add MutableTriple.ofNonNull(L, M, R). Thanks to Gary Gregory. +o Add Pair.ofNonNull(L, R). Thanks to Gary Gregory. +o Add Triple.ofNonNull(L, M, R). Thanks to Gary Gregory. +o Add ArrayUtils.containsAny(Object[], Object...). Thanks to Gary Gregory. +o Add Processor.Type.AARCH_64. Thanks to Gary Gregory. +o Add Processor.isAarch64(). Thanks to Gary Gregory. +o Update ArchUtils.getProcessor(String) for "aarch64". Thanks to Gary Gregory. +o Add JavaVersion.JAVA_18. Thanks to Gary Gregory. +o Add JavaVersion.JAVA_19. Thanks to Emmanuel Bourg. +o Add JavaVersion.JAVA_20. Thanks to Emmanuel Bourg. +o Add JavaVersion.JAVA_21. Thanks to Emmanuel Bourg. +o Add TimeZones.toTimeZone(TimeZone). Thanks to Gary Gregory. +o Add FutureTasks. Thanks to Gary Gregory. +o Add Memoizer(Function) and Memoizer(Function, boolean). Thanks to Gary Gregory. +o Add Consumers. Thanks to Gary Gregory. +o Add github/codeql-action. Thanks to Gary Gregory. +o Add coverage.yml. Thanks to Gary Gregory. +o Add DurationUtils.since(Temporal). Thanks to Gary Gregory. +o Add DurationUtils.of(FailableConsumer|FailableRunnbale). Thanks to Gary Gregory. +o Add ExceptionUtils.forEach(Throwable, Consumer). Thanks to Gary Gregory. +o Add ExceptionUtils.stream(Throwable). Thanks to Gary Gregory. +o Add ExceptionUtils.getRootCauseStackTraceList(Throwable). Thanks to Gary Gregory. +o Add SystemUtils.IS_OS_WINDOWS_11. Thanks to Will Herrmann, Gary Gregory, Roland Kreuzer. +o Add SystemUtils.IS_JAVA_16. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_17. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_18. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_19. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_20. Thanks to Gary Gregory. +o Add SystemUtils.IS_JAVA_21. Thanks to Emmanuel Bourg. +o LANG-1627: Add ArrayUtils.oneHot(). Thanks to Alberto Scotto, Avijit Chakraborty, Steve Bosman, Bruno P. Kinoshita, Gary Gregory. +o LANG-1662: Let ReflectionToStringBuilder only reflect given field names #849. Thanks to Daniel Augusto Veronezi Salvador, Gary Gregory, Bruno P. Kinoshita. +o Add Streams.of(Enumeration). Thanks to Gary Gregory. +o Add Streams.of(Iterable). Thanks to Gary Gregory. +o Add Streams.of(Iterator). Thanks to Gary Gregory. +o LANG-1689: Simple support for Optional in ObjectUtils#isEmpty() #933. Thanks to Joseph Hendrix, Gary Gregory. +o Add Processor.Type.getLabel(). Thanks to Gary Gregory. +o Add Processor.toString(). Thanks to Gary Gregory. +o Add HashCodeBuilder.equals(Object). Thanks to Gary Gregory. +o Add BooleanUtils.values() and forEach(). Thanks to Gary Gregory. +o Add ClassPathUtils.packageToPath(String) and pathToPackage(String) Thanks to Gary Gregory. +o Add CalendarUtils#getDayOfYear() #968 Thanks to Arturo Bernal. +o Add NumberRange, DoubleRange, IntegerRange, LongRange. Thanks to Gary Gregory. +o Add missing exception javadoc/tests for some null arguments #869. Thanks to Diego Marcilio, Bruno P. Kinoshita, Gary Gregory. +o Add ClassLoaderUtils.getSystemURLs() and getThreadURLs(). Thanks to Gary Gregory. +o Add RegExUtils.dotAll() and dotAllMatcher(). Thanks to Gary Gregory. +o Add Pair.accept(FailableBiConsumer). Thanks to Gary Gregory. +o Add Pair.apply(FailableBiFunction). Thanks to Gary Gregory. +o LANG-1677: Add ReflectionDiffBuilder.setExcludeFieldNames(...) and DiffExclude a? #838. Thanks to Dennis Baerten, Gary Gregory. +o LANG-1647: Add and ExceptionUtils.isChecked() and isUnchecked() #1069 Thanks to Arturo Bernal, Dimitrios Efthymiou, Gary Gregory. +o Add and use ExceptionUtils.throwUnchecked(throwable). Thanks to Gary Gregory. +o Add LockingVisitors.create(O, ReadWriteLock). Thanks to Gary Gregory. + +Fixed Bugs: +o LANG-1645: NumberUtils.createNumber() to recognize hex integers prefixed with +. Thanks to Alex Herbert. +o LANG-1646: NumberUtils.createNumber() to return requested floating point type for zero. Thanks to Alex Herbert. +o DMI: Random object created and used only once (DMI_RANDOM_USED_ONLY_ONCE); Better multi-threaded behavior. Thanks to SpotBugs, Gary Gregory. +o LANG-1646: Redundant Collection operation. Use Collections.emptyIterator() #738. Thanks to Arturo Bernal. +o Make Streams.stream(Collection) null-safe. Thanks to Gary Gregory. +o LANG-1667: Allow tests to access java.util classes such as ArrayList in Java 16 #788. Thanks to Andrew Thomas. +o LANG-1669: OpenJDK 16 Day Period Parsing #791. Thanks to Andrew Thomas. +o LANG-1663: Update documentation to list correct exception for null array parameters #785. Thanks to Andrew Thomas. +o Fixing reversed Javadoc descriptions in StopWatch #781. Thanks to Thunderforge. +o LANG-1670: Fix typos in JavaDoc #795. Thanks to Igor Shuvalov. +o Simplify assertions with equivalent but more simple. #792. Thanks to Arturo Bernal. +o Avoid multiple equivalent occurrences of the same expression. #797. Thanks to Arturo Bernal. +o Remove redundant initializers #800. Thanks to Arturo Bernal. +o Fix ObjectUtils Javadocs #755. Thanks to Arturo Bernal. +o Add test idea for RangeTest from PR #815 by Rushi98, but with a new comment. Thanks to Rushi98, Gary Gregory. +o LANG-1674: Make Range constructors more generic #810. Thanks to singhbaljit, Gary Gregory. +o Use final and Remove redundant String. #813, #816. Thanks to Arturo Bernal. +o Use Set instead of List for checking the contains() method #734. Thanks to CiprianBodnarescu. +o Javadoc for StringUtils.substringBefore(String str, int separator) doesn't mention that the separator is an int. Thanks to Roland Kreuzer. +o Fix NullPointerException in ThreadUtils.getSystemThreadGroup() when the current thread is stopped. Thanks to Gary Gregory. +o ArrayUtils.toPrimitive(Boolean...) null array elements map to false, like Boolean.parseBoolean(null) and its callers return false. Thanks to Gary Gregory. +o StrBuilder.StrBuilderReader.skip(long): Throw an exception when an implicit narrowing conversion in a compound assignment would result in information loss or a numeric error such as an overflows. Thanks to CodeQL, Gary Gregory. +o Deprecate Validate#notNull(Object) in favor of using Objects#requireNonNull(Object, String). Thanks to Gary Gregory. +o LANG-1462: Use TimeZone from calendar in DateFormatUtils. Thanks to Lijun Liang, Arun Avanathan, Tai Dupree, Maria Buiakova, Gary Gregory. +o Updating javadoc for NullPointerException when Validate.notNull() is called #870. Thanks to Diego Marcilio. +o Fixing and adding DateUtils exception Javadocs #871. Thanks to Diego Marcilio. +o LANG-1679: Improve performance of StringUtils.unwrap(String, String) #844. Thanks to clover. +o LANG-1675: Improve performance of StringUtils.join for primitives #812. Thanks to clover. +o LANG-1675: Fixed NPE getting Stack Trace if Throwable is null #733. Thanks to Arturo Bernal. +o Make Validate.isAssignableFrom() check null inputs. Thanks to Gary Gregory, Arturo Bernal. +o Fix Javadoc for Validate.isAssignableFrom(). Thanks to Arturo Bernal. +o Make final mappingFunction variable #876. Thanks to Arturo Bernal. +o Remove unnecessary variable creations #882. Thanks to Arturo Bernal. +o Minor changes #769. Thanks to Arturo Bernal. +o LANG-1680: FastDateFormat does not support the 'L'-Pattern from SimpleDateFormat. Thanks to Michael Krause, Steve Bosman, Gary Gregory. +o Increase test coverage of ComparableUtils from 71% to 100% #898. Thanks to Steve Bosman, Gary Gregory. +o Increase method test coverage of MultilineRecursiveToStringStyle #899. Thanks to Steve Bosman. +o Fix unstable coverage of CharSequenceUtils tests noticed during merge of PRs 898 and 899 #901. Thanks to Steve Bosman. +o Rewrite Conversion.binaryBeMsb0ToHexDigit to invert logic of binaryToHexDigit. Thanks to Arturo Bernal. +o Allow extension of previously final classes ImmutablePair and ImmutableTriple. Thanks to Gary Gregory. +o Update ClassUtils Javadoc with some missing throws NPE #912. Thanks to shalk, Bruno P. Kinoshita, Gary Gregory. +o Javadoc: StringUtils.repeat("", "x", 3) = "xx"; #918. Thanks to guicaiyue. +o Fix typos #920, #923. Thanks to Marc Wrobel. +o Simplify condition #925. Thanks to Bhimantoro Suryo Admodjo. +o StringUtils.join(Iterable, String) should only return null when the Iterable is null. Thanks to Gary Gregory. +o StringUtils.join(Iterator, String) should only return null when the Iterator is null. Thanks to Gary Gregory. +o Add tests to increase coverage #904. Thanks to Arturo Bernal. +o Extends Object clauses are redundant #937. Thanks to Arturo Bernal. +o Simplify conditional expression. #941. Thanks to Arturo Bernal. +o Fix some Javadoc comments #938. Thanks to Arturo Bernal. +o Deprecate getNanosOfMiili() method with typo and create proper getNanosOfMilli() #940. Thanks to Arturo Bernal, Gary Gregory. +o Deprecate ThreadUtils code that defines custom function interfaces in favor of stock java.util.function.Predicate usage. Thanks to Gary Gregory. +o Fix links in Javadoc and documentation #926. Thanks to Marc Wrobel. +o LANG-1604: Deprecate RandomUtils in favor of Apache Commons RNG UniformRandomProvider #942. Thanks to Gilles Sadowski, Maksym Bohachov, Gary Gregory. +o LANG-1638: Added docs regarding week year support #924. Thanks to Shailendra Soni, Michael Osipov, Arun Avanathan, Andrew Thomas, Bruno P. Kinoshita, Gary Gregory. +o LANG-1691: ClassUtils.getShortCanonicalName doesn't use the canonicalName #949. Thanks to Thiyagarajan, Gary Gregory. +o Validate: Get error messages without using String.format when varargs is empty. Thanks to Piotr Stawirej. +o Simplify expression (length is never < 0) #962. Thanks to Arturo Bernal. +o Fix simple broken javadoc. #981. Thanks to Arturo Bernal. +o Fix typo #1001. Thanks to LeeJuHyun. +o Use Objects.requireNonNull() directly #1022. Thanks to Arturo Bernal. +o LANG-1694: MethodUtils.getMatchingMethod() fails with "Found multiple candidates" #1033. Thanks to SeasonPan. +o LANG-1643: Construct ArrayList with better default size #1041. Thanks to laurentschoelens. +o ThreadUtilsTest#testThreadGroups will test failed when using Junit5 parallel test #1051. Thanks to remeio. +o Swap the order of assertion args (first excepted then actual) #1054. Thanks to remeio. +o Fix the comment of Failable, redundant "-" #1056. Thanks to remeio. +o Fix the comment of ComparableUtils, using "smallest", not "largest" #1058. Thanks to remeio. +o AnnotationUtilsTest and FormattableUtilsTest Only use static imports to import assert methods in tests #1052. Thanks to remeio. +o [LANG-1681] Fix some FieldUtils Javadocs #1047. Thanks to laurentschoelens, Bruno P. Kinoshita, Diego Marcilio. +o Remove unnecessary statement in DurationFormatUtils #965. Thanks to Arturo Bernal. +o LANG-1699: Corrected value of SystemUtils.JAVA_VENDOR #1066. Thanks to Darren Coleman. +o [StepSecurity] ci: Harden GitHub Actions #1067. Thanks to step-security-bot, Gary Gregory. +o Update Javadoc for the insert methods in ArrayUtils #1078. Thanks to Dimitrios Efthymiou. +o Deprecate ExceptionUtils.ExceptionUtils(). Thanks to Gary Gregory. +o LANG-1697: TypeUtils.getRawType() throws a NullPointerException on Wildcard GenericArrayType. Thanks to Jan Arne Sparka, Gary Gregory. +o Throw IllegalArgumentException instead of InternalError in the builder package. Thanks to Gary Gregory. +o Avoid NPE in MutableObject#equals() for null content. Thanks to Gary Gregory. +o SystemUtils fix and updates related to macOS #1085. Thanks to Ali Khaleqi Yekta, Gary Gregory. + +Changes: +o Bump actions/cache from 2.1.4 to 3.0.10 #742, #752, #764, #833, #867, #959, #964. Thanks to Dependabot, XenoAmess, Gary Gregory. +o Bump actions/checkout from 2 to 3.1.0 #819, #825, #859, #963. Thanks to Dependabot, Gary Gregory. +o Bump actions/setup-java from v1.4.3 to 3.5.1 #879. Thanks to Gary Gregory. +o Bump spotbugs-maven-plugin from 4.2.0 to 4.7.3.0 #735, #808, #822, #834, #868, #895, #919, #927, #946, #989. Thanks to Dependabot, Gary Gregory. +o Bump spotbugs from 4.2.2 to 4.7.3 #744, #917, #947, #973. Thanks to Dependabot, Gary Gregory. +o Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #943. Thanks to Dependabot, Gary Gregory. +o Bump checkstyle from 8.41 to 9.3 #739, #768, #787, #811, #824, #843. Thanks to Dependabot, Gary Gregory. +o Bump easymock from 4.2 to 5.1.0 #746, #972, #986, #1012. Thanks to Dependabot. +o Bump commons.jacoco.version from 0.8.6 to 0.8.8. Thanks to Gary Gregory. +o Bump commons.japicmp.version from 0.15.2 to 0.16.0. Thanks to Gary Gregory. +o Bump junit-pioneer from 1.3.8 to 1.9.1 #749, #767, #832, #883, #988, #991, #995. Thanks to Dependabot, Gary Gregory. +o Bump junit-bom from 5.7.1 to 5.9.1 #761, #805, #807, #836, #928, #955. Thanks to Dependabot. +o Bump maven-javadoc-plugin from 3.2.0 to 3.4.1. Thanks to Dependabot, Gary Gregory. +o Bump jmh.version from 1.27 to 1.36 #794, #842, #872, #990. Thanks to Dependabot. +o Bump maven-pmd-plugin from 3.14.0 to 3.19.0 #802, #858, #909, #948. Thanks to Dependabot. +o Bump pmd from 6.40.0 to 6.52.0 #837, #861, #873, #905, #915, #932, #944. Thanks to Dependabot. +o Bump biz.aQute.bndlib from 5.3.0 to 6.3.1 #814, #835. Thanks to Dependabot, Gary Gregory. +o Bump maven-bundle-plugin from 5.1.1 to 5.1.2. Thanks to Dependabot. +o Bump animal-sniffer-maven-plugin from 1.19 to 1.21. Thanks to Dependabot. +o Bump exec-maven-plugin from 1.6.0 to 3.1.0 #590, #922. Thanks to Dependabot. +o Bump maven-surefire-plugin from 3.0.0-M5 to 3.0.0-M7 #880, #910. Thanks to Dependabot. +o Bump apache-rat from 0.13 to 0.14. Thanks to Gary Gregory. +o Bump commons-parent from 53 to 58 #954, #1000, #1011, #1061. Thanks to Dependabot, Gary Gregory. +o Bump commons-text from 1.9 to 1.10.0 #957. Thanks to Dependabot. +o Bump commons.pmd-impl.version from 6.49.0 to 6.51.0 #961. Thanks to Dependabot, Gary Gregory. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +-Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.12.0 Release Notes +---------------------------------------- + +INTRODUCTION: + +This document contains the release notes for the 3.12.0 version of Apache Commons Lang. +Commons Lang is a set of utility functions and reusable components that should be of use in any +Java environment. + +Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8. + +For the advice on upgrading from 2.x to 3.x, see the following page: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +New features and bug fixes. + +Changes in this version include: + +New features: +o Add BooleanUtils.booleanValues(). Thanks to Gary Gregory. +o Add BooleanUtils.primitiveValues(). Thanks to Gary Gregory. +o LANG-1535: Add StringUtils.containsAnyIgnoreCase(CharSequence, CharSequence...). Thanks to Gary Gregory, Isira Seneviratne. +o LANG-1359: Add StopWatch.getStopTime(). Thanks to Gary Gregory, Keegan Witt. +o More test coverage for CharSequenceUtils. #631. Thanks to Edgar Asatryan. +o Add fluent-style ArraySorter. Thanks to Gary Gregory. +o Add and use LocaleUtils.toLocale(Locale) to avoid NPEs. Thanks to Gary Gregory. +o Add FailableShortSupplier, handy for JDBC APIs. Thanks to Gary Gregory. +o Add JavaVersion.JAVA_17. Thanks to Gary Gregory. +o LANG-1636: Add missing boolean[] join method #686. Thanks to . +o Add StringUtils.substringBefore(String, int). Thanks to Gary Gregory. +o Add Range.INTEGER. Thanks to Gary Gregory. +o Add DurationUtils. Thanks to Gary Gregory. +o Introduce the use of @Nonnull, and @Nullable, and the Objects class as a helper tool. +o Add and use true and false String constants #714. Thanks to Arturo Bernal, Gary Gregory. +o Add and use ObjectUtils.requireNonEmpty() #716. Thanks to Arturo Bernal, Gary Gregory. + +Fixed Bugs: +o LANG-1592: Correct implementation of RandomUtils.nextLong(long, long) Thanks to Huang Pingcai, Alex Herbert. +o LANG-1600: Restore handling of collections for non-JSON ToStringStyle #610. Thanks to Michael F. +o ContextedException Javadoc add missing semicolon #581. Thanks to iamchao1129. +o LANG-1608: Resolve JUnit pioneer transitive dependencies using JUnit BOM. Thanks to Edgar Asatryan. +o NumberUtilsTest - incorrect types in min/max tests #634. Thanks to HubertWo, Gary Gregory. +o LANG-1579: Improve StringUtils.stripAccents conversion of remaining accents. Thanks to XenoAmess. +o LANG-1606: StringUtils.countMatches - clarify Javadoc. Thanks to Rustem Galiev. +o LANG-1591: Remove redundant argument from substring call. Thanks to bhawna94. +o LANG-1613: BigDecimal is created when you pass it the min and max values, #642. Thanks to Arturo Bernal, Gary Gregory. +o LANG-1541: ArrayUtils.contains() and indexOf() fail to handle Double.NaN #647. Thanks to Arturo Bernal, Gary Gregory. +o LANG-1615: ArrayUtils contains() and indexOf() fail to handle Float.NaN # #561. Thanks to Arturo Bernal, Gary Gregory. +o Fix potential NPE in TypeUtils.isAssignable(Type, ParameterizedType, Map, Type>). Thanks to Gary Gregory. +o LANG-1420: TypeUtils.isAssignable returns wrong result for GenericArrayType and ParameterizedType, #643. Thanks to Gordon Fraser, Rostislav Krasny, Arturo Bernal, Gary Gregory. +o LANG-1612: testGetAllFields and testGetFieldsWithAnnotation sometimes fail. Thanks to XinT, Gary Gregory. +o Fix Javadoc for SystemUtils.isJavaVersionAtMost() #638. Thanks to John R. D'Orazio. +o LANG-1610: Fix StringUtils.unwrap throws StringIndexOutOfBoundsException #636. Thanks to Tony Liang. +o Fix formatting of isAnyBlank() and isAnyEmpty(). #513. Thanks to Isira Seneviratne. +o LANG-1618: TypeUtils. containsTypeVariables does not support GenericArrayType #661. Thanks to Arturo Bernal. +o LANG-1622: Javadoc of some methods incorrectly refers to another method, #667, #668. #670. Thanks to Kanak Sony, anomen-s. +o LANG-1620: Refine StringUtils.lastIndexOfIgnoreCase #664. Thanks to Arturo Bernal. +o LANG-1619: Refine StringUtils.abbreviate #663. Thanks to Arturo Bernal. +o LANG-1584: Refine StringUtils.isNumericSpace #573. Thanks to Arturo Bernal. +o LANG-1580: Refine StringUtils.deleteWhitespace #569. Thanks to Arturo Bernal. +o LANG-1626: Correction in Javadoc of some methods. #673 Thanks to Kanak Sony. +o LANG-1628: Javadoc for RandomStringUtils.random() letters, numbers parameters is wrong. Thanks to Jarkko Rantavuori. +o Correct markup in Javadoc for unbalanced braces #679. Thanks to Felix Schumacher. +o LANG-1544: MethodUtils.invokeMethod NullPointerException in case of null in args list #680. Thanks to Peter Nagy, Michael Buck, Gary Gregory. +o LANG-1637: Fix 2 digit week year formatting #688. Thanks to Uri Gonen, Gary Gregory, Michael Osipov. +o Fix broken Javadoc links to commons-text #712. Thanks to Chris Smowton. +o Add and use ThreadUtils.sleep(Duration). Thanks to Gary Gregory. +o Add and use ThreadUtils.join(Thread, Duration). Thanks to Gary Gregory. +o Add ObjectUtils.wait(Duration). Thanks to Gary Gregory. + +Changes: +o LANG-1596: ArrayUtils.toPrimitive(Object) does not support boolean and other types #607. Thanks to Richard Eckart de Castilho. +o Enable Dependabot #587. Thanks to Gary Gregory. +o Bump junit-jupiter from 5.6.2 to 5.7.0. +o Bump spotbugs from 4.1.2 to 4.2.1, #627, #671, #708. Thanks to chtompki, Dependabot. +o Bump spotbugs-maven-plugin from 4.0.0 to 4.2.0, #593, #596, #609, #623, #632, #692. Thanks to Dependabot. +o Bump biz.aQute.bndlib from 5.1.1 to 5.3.0 #592, #628, #715. Thanks to Dependabot. +o Bump junit-pioneer from 0.6.0 to 1.1.0, #589, #597, #600, #624, #625, #662. Thanks to Dependabot. +o Bump checkstyle from 8.34 to 8.40, #594, #614, #637, #665, #706. Thanks to Dependabot. +o Bump actions/checkout from v2.3.1 to v2.3.4 #601, #639. Thanks to Dependabot. +o Bump actions/setup-java from v1.4.0 to v1.4.2 #612. Thanks to Dependabot. +o Update commons.jacoco.version 0.8.5 to 0.8.6 (Fixes Java 15 builds). Thanks to Gary Gregory. +o Update maven-surefire-plugin 2.22.2 -> 3.0.0-M5. Thanks to Gary Gregory. +o Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #660. Thanks to Dependabot. +o Bump jmh.version from 1.21 to 1.27 #674. Thanks to Dependabot. +o Update commons.japicmp.version 0.14.3 -> 0.15.2. Thanks to Gary Gregory. +o Processor.java: check enum equality with == instead of .equals() method #690. Thanks to Ali K. Nouri. +o Bump junit-pioneer from 1.1.0 to 1.3.0 #702. Thanks to Dependabot. +o Bump maven-checkstyle-plugin from 3.1.1 to 3.1.2 #705. Thanks to Dependabot. +o Bump actions/cache from v2 to v2.1.4 #710. Thanks to Dependabot. +o Bump junit-bom from 5.7.0 to 5.7.1 #707. Thanks to Dependabot. +o Minor Improvements #701. Thanks to Arturo Bernal. +o Minor Improvement: Add final variable.try to make the code read-only #700. Thanks to Arturo Bernal. +o Minor Improvement: Remove redundant initializer #699. Thanks to Arturo Bernal. +o Use own validator ObjectUtils.anyNull to check null String input #718. Thanks to Arturo Bernal. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +-Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.11.0 Release Notes +---------------------------------------- + + +INTRODUCTION: + +This document contains the release notes for the 3.11 version of Apache Commons Lang. +Commons Lang is a set of utility functions and reusable components that should be of use in any +Java environment. + +Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8. + +For the advice on upgrading from 2.x to 3.x, see the following page: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +New features and bug fixes. + +Changes in this version include: + +New features: +o Add ArrayUtils.isSameLength() to compare more array types #430. Thanks to XenoAmess, Gary Gregory. +o Added the Locks class as a convenient possibility to deal with locked objects. +o LANG-1568: Add to Functions: FailableBooleanSupplier, FailableIntSupplier, FailableLongSupplier, FailableDoubleSupplier, and so on. +o LANG-1569: Add ArrayUtils.get(T[], index, T) to provide an out-of-bounds default value. +o LANG-1570: Add JavaVersion enum constants for Java 14 and 15. #553. Thanks to Edgar Asatryan. +o Add JavaVersion enum constants for Java 16. Thanks to Gary Gregory. +o LANG-1556: Use Java 8 lambdas and Map operations. Thanks to XenoAmess. +o LANG-1565: Change removeLastFieldSeparator to use endsWith #550. Thanks to XenoAmess. +o LANG-1557: Change a Pattern to a static final field, for not letting it compile each time the function invoked. #542. Thanks to XenoAmess, Gary Gregory. +o Add ImmutablePair factory methods left() and right(). +o Add ObjectUtils.toString(Object, Supplier). +o Add org.apache.commons.lang3.StringUtils.substringAfter(String, int). +o Add org.apache.commons.lang3.StringUtils.substringAfterLast(String, int). + +Fixed Bugs: +o Fix Javadoc for StringUtils.appendIfMissingIgnoreCase() #507. Thanks to contextshuffling. +o LANG-1560: Refine Javadoc #545. Thanks to XenoAmess. +o LANG-1554: Fix typos #539. Thanks to XenoAmess. +o LANG-1555: Ignored exception `ignored`, should not be called so #540. Thanks to XenoAmess. +o LANG-1528: StringUtils.replaceEachRepeatedly gives IllegalStateException #505. Thanks to Edwin Delgado H. +o LANG-1543: [JSON string for maps] ToStringBuilder.reflectionToString doesnt render nested maps correctly. Thanks to Swaraj Pal, Wander Costa, Gary Gregory. +o Correct Javadocs of methods that use Validate.notNull() and replace some uses of Validate.isTrue() with Validate.notNull(). #525. Thanks to Isira Seneviratne. +o LANG-1539: Add allNull() and anyNull() methods to ObjectUtils. #522. Thanks to Isira Seneviratne. + +Changes: +o Refine test output for FastDateParserTest Thanks to Jin Xu. +o LANG-1549: CharSequenceUtils.lastIndexOf : remake it Thanks to Jin Xu. +o remove encoding and docEncoding and use inherited values from commons-parent Thanks to XenoAmess. +o Simplify null checks in Pair.hashCode() using Objects.hashCode(). #517. Thanks to Isira Seneviratne, Bruno P. Kinoshita. +o Simplify null checks in Triple.hashCode() using Objects.hashCode(). #516. Thanks to Isira Seneviratne, Bruno P. Kinoshita. +o Simplify some if statements in StringUtils. #521. Thanks to Isira Seneviratne, Bruno P. Kinoshita. +o LANG-1537: Simplify a null check in the private replaceEach() method of StringUtils. #514. Thanks to Isira Seneviratne, Bruno P. Kinoshita. +o LANG-1534: Replace some usages of the ternary operator with calls to Math.max() and Math.min() #512. Thanks to Isira Seneviratne, Bruno P. Kinoshita. +o (Javadoc) Fix return tag for throwableOf*() methods #518. Thanks to Arend v. Reinersdorff, Bruno P. Kinoshita. +o LANG-1545: CharSequenceUtils.regionMatches is wrong dealing with Georgian. Thanks to XenoAmess, Gary Gregory. +o LANG-1550: Optimize ArrayUtils::isArrayIndexValid method. #551. Thanks to Edgar Asatryan. +o LANG-1561: Use List.sort instead of Collection.sort #546. Thanks to XenoAmess. +o LANG-1563: Use StandardCharsets.UTF_8 #548. Thanks to XenoAmess. +o LANG-1564: Use Collections.singletonList insteadof Arrays.asList when there be only one element. #549. Thanks to XenoAmess. +o LANG-1553: Change array style from `int a[]` to `int[] a` #537. Thanks to XenoAmess. +o LANG-1552: Change from addAll to constructors for some List #536. Thanks to XenoAmess. +o LANG-1558: Simplify if as some conditions are covered by others #543. Thanks to XenoAmess. +o LANG-1567: Fixed Javadocs for setTestRecursive() #556. Thanks to Miguel MuīŋŊoz, Bruno P. Kinoshita, Gary Gregory. +o LANG-1542: ToStringBuilder.reflectionToString - Wrong JSON format when object has a List of Enum. Thanks to Tr?n Ng?c Khoa, Gary Gregory. +o Make org.apache.commons.lang3.CharSequenceUtils.toCharArray(CharSequence) public. +o org.apache.commons:commons-parent 50 -> 51. +o org.junit-pioneer:junit-pioneer 0.5.4 -> 0.6.0. +o org.junit.jupiter:junit-jupiter 5.6.0 -> 5.6.2. +o com.github.spotbugs:spotbugs 4.0.0 -> 4.0.6. +o com.puppycrawl.tools:checkstyle 8.29 -> 8.34. +o commons.surefire.version 3.0.0-M4 -> 3.0.0-M5.. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +Have fun! +-Apache Commons Team + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.10.0 Release Notes +---------------------------------------- + + +INTRODUCTION: + +This document contains the release notes for the 3.10 version of Apache Commons Lang. +Commons Lang is a set of utility functions and reusable components that should be of use in any +Java environment. + +Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8. + +For the advice on upgrading from 2.x to 3.x, see the following page: + + https://commons.apache.org/lang/article3_0.html + +Apache Commons Lang, a package of Java utility classes for the +classes that are in java.lang's hierarchy, or are considered to be so +standard as to justify existence in java.lang. + +New features and bug fixes. Requires Java 8, supports Java 9, 10, 11. + +Changes in this version include: + +New features: +o LANG-1457: Add ExceptionUtils.throwableOfType(Throwable, Class) and friends. +o LANG-1458: Add EMPTY_ARRAY constants to classes in org.apache.commons.lang3.tuple. +o LANG-1461: Add null-safe StringUtils APIs to wrap String#getBytes([Charset|String]). +o LANG-1467: Add zero arg constructor for org.apache.commons.lang3.NotImplementedException. +o LANG-1470: Add ArrayUtils.addFirst() methods. +o LANG-1479: Add Range.fit(T) to fit a value into a range. +o LANG-1477: Added Functions.as*, and tests thereof, as suggested by Peter Verhas +o LANG-1485: Add getters for lhs and rhs objects in DiffResult #451. Thanks to nicolasbd. +o LANG-1486: Generify builder classes Diffable, DiffBuilder, and DiffResult #452. Thanks to Gary Gregory. +o LANG-1487: Add ClassLoaderUtils with toString() implementations #453. Thanks to Gary Gregory. +o LANG-1489: Add null-safe APIs as StringUtils.toRootLowerCase(String) and StringUtils.toRootUpperCase(String) #456. Thanks to Gary Gregory. +o LANG-1494: Add org.apache.commons.lang3.time.Calendars. Thanks to Gary Gregory. +o LANG-1495: Add EnumUtils getEnum() methods with default values #475. Thanks to Cheong Voon Leong. +o LANG-1177: Added indexesOf methods and simplified removeAllOccurences #471. Thanks to Liel Fridman. +o LANG-1498: Add support of lambda value evaluation for defaulting methods #416. Thanks to Lysergid, Gary Gregory. +o LANG-1503: Add factory methods to Pair classes with Map.Entry input. #454. Thanks to XenoAmess, Gary Gregory. +o LANG-1505: Add StopWatch convenience APIs to format times and create a simple instance. Thanks to Gary Gregory. +o LANG-1506: Allow a StopWatch to carry an optional message. Thanks to Gary Gregory. +o LANG-1507: Add ComparableUtils #398. Thanks to Sam Kruglov, Mark Dacek, Marc Magon, Pascal Schumacher, Rob Tompkins, Bruno P. Kinoshita, Amey Jadiye, Gary Gregory. +o LANG-1508: Add org.apache.commons.lang3.SystemUtils.getUserName(). Thanks to Gary Gregory. +o LANG-1509: Add ObjectToStringComparator. #483. Thanks to Gary Gregory. +o LANG-1510: Add org.apache.commons.lang3.arch.Processor.Arch.getLabel(). Thanks to Gary Gregory. +o LANG-1512: Add IS_JAVA_14 and IS_JAVA_15 to org.apache.commons.lang3.SystemUtils. Thanks to Gary Gregory. +o LANG-1513: ObjectUtils: Get first non-null supplier value. Thanks to Bernhard Bonigl, Gary Gregory. +o Added the Streams class, and Functions.stream() as an accessor thereof. + +Fixed Bugs: +o LANG-1514: Make test more stable by wrapping assertions in hashset. Thanks to contextshuffling. +o LANG-1450: Generate Javadoc jar on build. +o LANG-1460: Trivial: year of release for 3.9 says 2018, should be 2019 Thanks to Larry West. +o LANG-1476: Use synchronize on a set created with Collections.synchronizedSet before iterating Thanks to emopers. +o LANG-1475: StringUtils.unwrap incorrect throw StringIndexOutOfBoundsException. Thanks to stzx. +o LANG-1406: StringIndexOutOfBoundsException in StringUtils.replaceIgnoreCase #423. Thanks to geratorres. +o LANG-1453: StringUtils.removeIgnoreCase("?a", "a") throws IndexOutOfBoundsException #423. Thanks to geratorres. +o LANG-1426: Corrected usage examples in Javadocs #458. Thanks to Brower, Mikko Maunu, Suraj Gautam. +o LANG-1463: StringUtils abbreviate returns String of length greater than maxWidth #477. Thanks to bbeckercscc, Gary Gregory. +o LANG-1500: Test may fail due to a different order of fields returned by reflection api #480. Thanks to contextshuffling. +o LANG-1501: Sort fields in ReflectionToStringBuilder for deterministic order #481. Thanks to contextshuffling. +o LANG-1433: MethodUtils will throw a NPE if invokeMethod() is called for a var-args method #407. Thanks to Christian Franzen. +o LANG-1518: MethodUtils.getAnnotation() with searchSupers = true does not work if super is generic #494. Thanks to Michele Preti, Bruno P. Kinoshita, Gary Gregory. + +Changes: +o LANG-1437: Remove redundant if statements in join methods #411. Thanks to Andrei Troie. +o commons.japicmp.version 0.13.1 -> 0.14.1. +o junit-jupiter 5.5.0 -> 5.5.1. +o junit-jupiter 5.5.1 -> 5.5.2. +o Improve Javadoc based on the discussion of the GitHub PR #459. Thanks to Jonathan Leitschuh, Bruno P. Kinoshita, Rob Tompkins, Gary Gregory. +o maven-checkstyle-plugin 3.0.0 -> 3.1.0. +o LANG-696: Update documentation related to the issue LANG-696 #449. Thanks to Peter Verhas. +o AnnotationUtils little cleanup #467. Thanks to Peter Verhas. +o Update test dependency: org.easymock:easymock 4.0.2 -> 4.1. Thanks to Gary Gregory. +o Update test dependency: org.hamcrest:hamcrest 2.1 -> 2.2. Thanks to Gary Gregory. +o Update test dependency: org.junit-pioneer:junit-pioneer 0.3.0 -> 0.4.2. Thanks to Gary Gregory. +o Update build dependency: com.puppycrawl.tools:checkstyle 8.18 -> 8.27. Thanks to Gary Gregory. +o Update POM parent: org.apache.commons:commons-parent 48 -> 50. Thanks to Gary Gregory. +o BooleanUtils Javadoc #469. Thanks to Peter Verhas. +o Functions Javadoc #466. Thanks to Peter Verhas. +o org.easymock:easymock 4.1 -> 4.2. Thanks to Gary Gregory. +o org.junit-pioneer:junit-pioneer 0.4.2 -> 0.5.4. Thanks to Gary Gregory. +o org.junit.jupiter:junit-jupiter 5.5.2 -> 5.6.0. Thanks to Gary Gregory. +o Use Javadoc {@code} instead of pre tags. #490. Thanks to Peter Verhas. +o ExceptionUtilsTest to 100% #486. Thanks to Peter Verhas. +o Reuse own code in Functions.java #493. Thanks to Peter Verhas. +o LANG-1523: Avoid unnecessary allocation in StringUtils.wrapIfMissing. #496. Thanks to Edgar Asatryan, Bruno P. Kinoshita, Gary Gregory. +o LANG-1525: Internally use Validate.notNull(foo, ...) instead of Validate.isTrue(foo != null, ...). Thanks to Edgar Asatryan, Bruno P. Kinoshita, Gary Gregory. +o LANG-1526: Add 1 and 0 in toBooleanObject(final String str) #502. Thanks to Dominik Schramm. +o LANG-1527: Remove an redundant argument check in NumberUtils #504. Thanks to Pengyu Nie. +o LANG-1529: Deprecate org.apache.commons.lang3.ArrayUtils.removeAllOccurences(*) for org.apache.commons.lang3.ArrayUtils.removeAllOccurrences(*). Thanks to Gary Gregory, BillCindy, Bruno P. Kinoshita. + + +Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html + +For complete information on Apache Commons Lang, including instructions on how to submit bug reports, +patches, or suggestions for improvement, see the Apache Commons Lang website: + +https://commons.apache.org/proper/commons-lang/ + +Download page: https://commons.apache.org/proper/commons-lang/download_lang.cgi + +----------------------------------------------------------------------------- + +Apache Commons Lang 3.9 Release Notes +------------------------------------- INTRODUCTION: @@ -43,15 +1411,14 @@ o (doc) Fix javadoc for 'startIndex' parameter of StringUtils.join() Historical list of changes: https://commons.apache.org/proper/commons-lang/changes-report.html For complete information on Apache Commons Lang, including instructions on how to submit bug reports, -patches, or suggestions for improvement, see the Apache Apache Commons Lang website: +patches, or suggestions for improvement, see the Apache Commons Lang website: https://commons.apache.org/proper/commons-lang/ -============================================================================= +----------------------------------------------------------------------------- - Apache Commons Lang - Version 3.8.1 - Release Notes +Apache Commons Lang 3.8.1 Release Notes +---------------------------------------- INTRODUCTION: @@ -78,11 +1445,10 @@ Changes in this version include: Fixed Bugs: o LANG-1419: Restore BundleSymbolicName for OSGi -============================================================================= +----------------------------------------------------------------------------- - Apache Commons Lang - Version 3.8 - Release Notes +Apache Commons Lang 3.8 Release Notes +------------------------------------- INTRODUCTION: @@ -107,7 +1473,7 @@ Changes in this version include: New features: o LANG-1352: EnumUtils.getEnumIgnoreCase and isValidEnumIgnoreCase methods added Thanks to Ruslan Sibgatullin. -o LANG-1372: Add ToStringSummary annotation Thanks to Sérgio Ozaki. +o LANG-1372: Add ToStringSummary annotation Thanks to SīŋŊrgio Ozaki. o LANG-1356: Add bypass option for classes to recursive and reflective EqualsBuilder Thanks to Yathos UG. o LANG-1391: Improve Javadoc for StringUtils.isAnyEmpty(null) Thanks to Sauro Matulli, Oleg Chubaryov. o LANG-1393: Add API SystemUtils.String getEnvironmentVariable(final String name, final String defaultValue) Thanks to Gary Gregory. @@ -137,12 +1503,10 @@ o LANG-1367: ObjectUtils.identityToString(Object) and friends should allocate bu o LANG-1405: Remove checks for java versions below the minimum supported one Thanks to Lars Grefer. o LANG-1402: Null/index safe get methods for ArrayUtils Thanks to Mark Dacek. -============================================================================= - - Apache Commons Lang - Version 3.7 - Release Notes +----------------------------------------------------------------------------- +Apache Commons Lang 3.7 Release Notes +---------------------------------------- INTRODUCTION: @@ -183,11 +1547,10 @@ o LANG-1346: Remove deprecation from RandomStringUtils o LANG-1361: ExceptionUtils.getThrowableList() is using deprecated ExceptionUtils.getCause() Thanks to Ana. -============================================================================= +----------------------------------------------------------------------------- - Apache Commons Lang - Version 3.6 - Release Notes +Apache Commons Lang 3.6 Release Notes +---------------------------------------- INTRODUCTION: @@ -288,7 +1651,7 @@ o LANG-660: Add methods to insert arrays into arrays at an index. o LANG-1034: Add support for recursive comparison to EqualsBuilder#reflectionEquals. Thanks to Yathos UG. o LANG-1067: Add a reflection-based variant of DiffBuilder. -o LANG-740: Implementation of a Memomizer. Thanks to James Sawle. +o LANG-740: Implementation of a Memoizer. Thanks to James Sawle. o LANG-1258: Add ArrayUtils#toStringArray method. Thanks to IG, Grzegorz Ro?niecki. o LANG-1160: StringUtils#abbreviate should support 'custom ellipses' parameter. @@ -311,7 +1674,7 @@ o LANG-1319: MultilineRecursiveToStringStyle StackOverflowError when object is an array. o LANG-1320: LocaleUtils#toLocale does not support language followed by UN M.49 numeric-3 area code followed by variant. -o LANG-1300: Clarify or improve behaviour of int-based indexOf methods in +o LANG-1300: Clarify or improve behavior of int-based indexOf methods in StringUtils. Thanks to Mark Dacek. o LANG-1286: RandomStringUtils random method can overflow and return characters outside of specified range. @@ -319,7 +1682,7 @@ o LANG-1292: WordUtils.wrap throws StringIndexOutOfBoundsException. o LANG-1287: RandomStringUtils#random can enter infinite loop if end parameter is to small. Thanks to Ivan Morozov. o LANG-1285: NullPointerException in FastDateParser$TimeZoneStrategy. - Thanks to Francesco Chicchiriccō. + Thanks to Francesco ChicchiriccīŋŊ. o LANG-1281: Javadoc of StringUtils.ordinalIndexOf is contradictory. Thanks to Andreas Lundblad. o LANG-1188: StringUtils#join(T...): warning: [unchecked] Possible heap @@ -367,9 +1730,10 @@ o LANG-1301: Moving apache-rat-plugin configuration into pluginManagement. Thanks to Karl Heinz Marbaise. o LANG-1316: Deprecate classes/methods moved to commons-text. -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.5 +Apache Commons Lang 3.5 Release Notes +---------------------------------------- HIGHLIGHTS @@ -388,7 +1752,7 @@ o Numerous extensions to org.apache.commons.lang3.StringUtils including truncation. o Added org.apache.commons.lang3.ThreadUtils - a utility class to work with instances of java.lang.Thread and java.lang.ThreadGroup. -o Added annotations @EqualsExclude, @HashCodeExclude and @ToStringEclude to +o Added annotations @EqualsExclude, @HashCodeExclude and @ToStringExclude to mark fields which should be ignored by the reflective builders in the org.apache.commons.lang3.builder package. o Support for various modify and retrieve value use cases added to the classes @@ -419,7 +1783,7 @@ JAVA 9 SUPPORT ============== Java 9 introduces a new version-string scheme. Details of this new scheme are -documented in JEP-223 (http://openjdk.java.net/jeps/223). In order to support +documented in JEP-223 (https://openjdk.org/jeps/223). In order to support JEP-223 two classes had to be changed: o org.apache.commons.lang3.JavaVersion @@ -439,7 +1803,7 @@ BUILDING ON JAVA 9 Java 8 introduced the Unicode Consortium's Common Locale Data Repository as alternative source for locale data. Java 9 will use the CLDR provider as -default provider for locale data (see http://openjdk.java.net/jeps/252). This +default provider for locale data (see https://openjdk.org/jeps/252). This causes an number of locale-sensitive test in Commons Lang to fail. In order to build Commons Lang 3.5 on Java 9, the locale provider has to be set to 'JRE': @@ -486,7 +1850,7 @@ o LANG-1168: Add SystemUtils.IS_OS_WINDOWS_10 property. Thanks to Pascal Schumacher. o LANG-1115: Add support for varargs in ConstructorUtils, MemberUtils, and MethodUtils. Thanks to Jim Lloyd and Joe Ferner. -o LANG-1134: Add methods to check numbers against NaN and inifinite to +o LANG-1134: Add methods to check numbers against NaN and infinite to Validate. Thanks to Alan Smithee. o LANG-1220: Add tests for missed branches in DateUtils. Thanks to Casey Scarborough. @@ -570,7 +1934,7 @@ o LANG-1205: NumberUtils.createNumber() behaves inconsistently with o LANG-1222: Fix for incorrect comment on StringUtils.containsIgnoreCase method. Thanks to Adam J. o LANG-1221: Fix typo on appendIfMissing javadoc. Thanks to Pierre Templier. -o LANG-1202: parseDateStrictly does't pass specified locale. Thanks to +o LANG-1202: parseDateStrictly doesn't pass specified locale. Thanks to Markus Jelsma. o LANG-1219: FastDateFormat doesn't respect summer daylight in some localized strings. Thanks to Jarek. @@ -637,9 +2001,9 @@ o LANG-1176: Improve ArrayUtils removeElements time complexity to O(n). Thanks to Jeffery Yuan. o LANG-1234: getLevenshteinDistance with a threshold: optimize implementation if the strings lengths differ more than the threshold. Thanks to - Jonatan Jönsson. + Jonatan JīŋŊnsson. o LANG-1151: Performance improvements for NumberUtils.isParsable. Thanks to - Juan Pablo Santos Rodríguez. + Juan Pablo Santos RodrīŋŊguez. o LANG-1218: EqualsBuilder.append(Object,Object) is too big to be inlined, which prevents whole builder to be scalarized. Thanks to Ruslan Cheremin. @@ -651,7 +2015,7 @@ o LANG-1182: Clarify Javadoc of StringUtils.containsAny(). Thanks to o LANG-1183: Making replacePattern/removePattern methods null safe in StringUtils. o LANG-1057: Replace StringBuilder with String concatenation for better - optimization. Thanks to Otávio Santana. + optimization. Thanks to OtīŋŊvio Santana. o LANG-1075: Deprecate SystemUtils.FILE_SEPARATOR and SystemUtils.PATH_SEPARATOR. o LANG-979: TypeUtils.parameterizeWithOwner - wrong format descriptor for @@ -666,10 +2030,10 @@ o LANG-1107: Fix parsing edge cases in FastDateParser. o LANG-1273: Added new property IS_OS_MAC_OSX_EL_CAPITAN in SystemUtils. Thanks to Jake Wang. -============================================================================= - - Release Notes for version 3.4 +----------------------------------------------------------------------------- +Apache Commons Lang 3.4 Release Notes +------------------------------------- COMPATIBILITY ============= @@ -698,13 +2062,13 @@ o LANG-1080: Add NoClassNameToStringStyle implementation of ToStringStyle. Thanks to Innokenty Shuvalov. o LANG-883: Add StringUtils.containsAny(CharSequence, CharSequence...) method. Thanks to Daniel Stewart. -o LANG-1052: Multiline recursive to string style. Thanks to Jan Matčrne. +o LANG-1052: Multiline recursive to string style. Thanks to Jan MatīŋŊrne. o LANG-536: Add isSorted() to ArrayUtils. Thanks to James Sawle. o LANG-1033: Add StringUtils.countMatches(CharSequence, char) o LANG-1021: Provide methods to retrieve all fields/methods annotated with a - specific type. Thanks to Alexander Müller. + specific type. Thanks to Alexander MīŋŊller. o LANG-1016: NumberUtils#isParsable method(s). Thanks to - Juan Pablo Santos Rodríguez. + Juan Pablo Santos RodrīŋŊguez. o LANG-999: Add fuzzy String matching logic to StringUtils. Thanks to Ben Ripkens. o LANG-994: Add zero copy read method to StrBuilder. Thanks to @@ -722,7 +2086,7 @@ o LANG-794: SystemUtils.IS_OS_WINDOWS_2008, VISTA are incorrect. Thanks to o LANG-1104: Parse test fails for TimeZone America/Sao_Paulo o LANG-948: Exception while using ExtendedMessageFormat and escaping braces. Thanks to Andrey Khobnya. -o LANG-1092: Wrong formating of time zones with daylight saving time in +o LANG-1092: Wrong formatting of time zones with daylight saving time in FastDatePrinter o LANG-1090: FastDateParser does not set error indication in ParsePosition o LANG-1089: FastDateParser does not handle excess hours as per @@ -762,7 +2126,7 @@ CHANGES o LANG-1102: Make logic for comparing OS versions in SystemUtils smarter o LANG-1091: Shutdown thread pools in test cases. Thanks to Fabian Lange. o LANG-1101: FastDateParser and FastDatePrinter support 'X' format -o LANG-1100: Avoid memory allocation when using date formating to StringBuffer. +o LANG-1100: Avoid memory allocation when using date formatting to StringBuffer. Thanks to mbracher. o LANG-935: Possible performance improvement on string escape functions. Thanks to Fabian Lange, Thomas Neidhart. @@ -802,9 +2166,10 @@ o LANG-1003: DurationFormatUtils are not able to handle negative o LANG-998: Javadoc is not clear on preferred pattern to instantiate FastDateParser / FastDatePrinter -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.3.2 +Apache Commons Lang 3.3.2 Release Notes +--------------------------------------- NEW FEATURES ============== @@ -816,9 +2181,10 @@ FIXED BUGS o LANG-992: NumberUtils#isNumber() returns false for "0.0", "0.4790", et al -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.3.1 +Apache Commons Lang 3.3.1 Release Notes +--------------------------------------- FIXED BUGS ============ @@ -832,9 +2198,10 @@ o LANG-982: DurationFormatUtils.formatDuration(61999, "s.SSSS") - ms field size should be 4 digits o LANG-978: Failing tests with Java 8 b128 -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.3 +Apache Commons Lang 3.3 Release Notes +------------------------------------- NEW FEATURES ============== @@ -879,12 +2246,12 @@ o LANG-943: Test DurationFormatUtilsTest.testEdgeDuration fails in o LANG-613: ConstructorUtils.getAccessibleConstructor() Does Not Check the Accessibility of Enclosing Classes o LANG-951: Fragments are wrong by 1 day when using fragment YEAR or MONTH. - Thanks to Sebastian Götz. + Thanks to Sebastian GīŋŊtz. o LANG-950: FastDateParser does not handle two digit year parsing like SimpleDateFormat o LANG-949: FastDateParserTest.testParses does not test FastDateParser o LANG-915: Wrong locale handling in LocaleUtils.toLocale(). - Thanks to Sergio Fernández. + Thanks to Sergio FernīŋŊndez. CHANGES ========= @@ -899,9 +2266,10 @@ o LANG-953: Convert package.html files to package-info.java files o LANG-940: Fix deprecation warnings o LANG-819: EnumUtils.generateBitVector needs a "? extends" -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.2.1 +Apache Commons Lang 3.2.1 Release Notes +--------------------------------------- BUG FIXES =========== @@ -913,9 +2281,10 @@ o LANG-942: Test failure in FastDateParserTest and FastDateFormat_ParserTest Henri Yandell. o LANG-938: Build fails with test failures when building with JDK 8 -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.2 +Apache Commons Lang 3.2 Release Notes +---------------------------------------- COMPATIBILITY WITH 3.1 ======================== @@ -983,7 +2352,7 @@ o LANG-462: FastDateFormat supports parse methods. BUG FIXES =========== -o LANG-932: Spelling fixes. Thanks to Ville Skyttä. +o LANG-932: Spelling fixes. Thanks to Ville SkyttīŋŊ. o LANG-929: OctalUnescaper tried to parse all of \279. o LANG-928: OctalUnescaper had bugs when parsing octals starting with a zero. o LANG-905: EqualsBuilder returned true when comparing arrays, even when the @@ -1021,7 +2390,7 @@ o LANG-831: FastDateParser does not handle white-space properly. o LANG-830: FastDateParser could use \Q \E to quote regexes. o LANG-828: FastDateParser does not handle non-Gregorian calendars properly. o LANG-826: FastDateParser does not handle non-ASCII digits correctly. -o LANG-822: NumberUtils#createNumber - bad behaviour for leading "--". +o LANG-822: NumberUtils#createNumber - bad behavior for leading "--". o LANG-818: FastDateFormat's "z" pattern does not respect timezone of Calendar instances passed to format(). o LANG-817: Add org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS_8. @@ -1075,15 +2444,16 @@ CHANGES WITHOUT TICKET o Fixed URLs in javadoc to point to new oracle.com pages -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.1 +Apache Commons Lang 3.1 Release Notes +------------------------------------- NEW FEATURES ============== o LANG-801: Add Conversion utility to convert between data types on byte level -o LANG-760: Add API StringUtils.toString(byte[] intput, String charsetName) +o LANG-760: Add API StringUtils.toString(byte[] input, String charsetName) o LANG-756: Add APIs ClassUtils.isPrimitiveWrapper(Class) and isPrimitiveOrWrapper(Class) o LANG-695: SystemUtils.IS_OS_UNIX doesn't recognize FreeBSD as a Unix system @@ -1110,9 +2480,10 @@ o LANG-748: Deprecating chomp(String, String) o LANG-736: CharUtils static final array CHAR_STRING is not needed to compute CHAR_STRING_ARRAY -============================================================================= +----------------------------------------------------------------------------- - Release Notes for version 3.0 +Apache Commons Lang 3.0 Release Notes +------------------------------------- ADDITIONS =========== @@ -1257,7 +2628,7 @@ o LANG-645: FastDateFormat.format() outputs incorrect week of year because o LANG-646: StringEscapeUtils.unescapeJava doesn't handle octal escapes and Unicode with extra u o LANG-656: Example StringUtils.indexOfAnyBut("zzabyycdxx", '') = 0 incorrect -o LANG-658: Some Entitys like Ö are not matched properly against its +o LANG-658: Some entities like Ö are not matched properly against its ISO8859-1 representation o LANG-659: EntityArrays typo: {"\u2122", "−"}, // minus sign, U+2212 ISOtech diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..744d4cddbb6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ + +The Apache Commons security page is [https://commons.apache.org/security.html](https://commons.apache.org/security.html). diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 0adaecc1eda..00000000000 --- a/checkstyle.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index 1505a02856c..143c1bb102b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,555 +18,110 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"> org.apache.commons commons-parent - 50 + 105 4.0.0 commons-lang3 - 3.10-SNAPSHOT + 3.21.0-SNAPSHOT Apache Commons Lang - 2001 - Apache Commons Lang, a package of Java utility classes for the - classes that are in java.lang's hierarchy, or are considered to be so - standard as to justify existence in java.lang. - + Apache Commons Lang is a package of Java utility classes for the Java lang packages, + classes that could be considered standard to justify their existence in java.lang. - https://commons.apache.org/proper/commons-lang/ + The code is tested using the latest revision of the JDK for supported LTS releases: 8, 11, 17, 21, and 25. + See https://github.com/apache/commons-lang/blob/master/.github/workflows/maven.yml + + Please ensure your build environment is up-to-date and kindly report any build issues. + Starting with Commons Lang 3.9, we target Java 8 and use those features. + + For advice on upgrading from 2.x to 3.x, see https://commons.apache.org/lang/article3_0.html + + https://commons.apache.org/proper/commons-lang/ + + github + https://github.com/apache/commons-lang/actions + jira https://issues.apache.org/jira/browse/LANG - - scm:git:http://gitbox.apache.org/repos/asf/commons-lang.git + scm:git:https://gitbox.apache.org/repos/asf/commons-lang.git scm:git:https://gitbox.apache.org/repos/asf/commons-lang.git - https://gitbox.apache.org/repos/asf?p=commons-lang.git - commons-lang-3.9 + https://gitbox.apache.org/repos/asf/commons-lang.git + rel/commons-lang-3.21.0 + + + + + org.junit.jupiter + junit-jupiter + test + + + org.junit-pioneer + junit-pioneer + test + + + org.easymock + easymock + 5.6.0 + test + + + org.mockito + mockito-inline + + + + org.apache.commons + commons-text + ${commons.text.version} + test + + + org.openjdk.jmh + jmh-core + ${commons.jmh.version} + test + + + org.openjdk.jmh + jmh-generator-annprocess + ${commons.jmh.version} + test + + + + + apache.website + Apache Commons Site + scm:svn:https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-lang/ + + + + + + 3.11.2 + - - - Daniel Rall - dlr - dlr@finemaltcoding.com - CollabNet, Inc. - - Java Developer - - - - Stephen Colebourne - scolebourne - scolebourne@joda.org - SITA ATS Ltd - 0 - - Java Developer - - - - Henri Yandell - bayard - bayard@apache.org - - - Java Developer - - - - Steven Caswell - scaswell - stevencaswell@apache.org - - - Java Developer - - -5 - - - Robert Burrell Donkin - rdonkin - rdonkin@apache.org - - - Java Developer - - - - Gary D. Gregory - ggregory - ggregory@apache.org - -5 - - Java Developer - - - - Fredrik Westermarck - fredrik - - - - Java Developer - - - - James Carman - jcarman - jcarman@apache.org - Carman Consulting, Inc. - - Java Developer - - - - Niall Pemberton - niallp - - Java Developer - - - - Matt Benson - mbenson - - Java Developer - - - - Joerg Schaible - joehni - joerg.schaible@gmx.de - - Java Developer - - +1 - - - Oliver Heger - oheger - oheger@apache.org - +1 - - Java Developer - - - - Paul Benedict - pbenedict - pbenedict@apache.org - - Java Developer - - - - Benedikt Ritter - britter - britter@apache.org - - Java Developer - - - - Duncan Jones - djones - djones@apache.org - 0 - - Java Developer - - - - Loic Guibert - lguibert - lguibert@apache.org - +4 - - Java Developer - - - - Rob Tompkins - chtompki - chtompki@apache.org - -5 - - Java Developer - - - - - - C. Scott Ananian - - - Chris Audley - - - Stephane Bailliez - - - Michael Becke - - - Benjamin Bentmann - - - Ola Berg - - - Nathan Beyer - - - Stefan Bodewig - - - Janek Bogucki - - - Mike Bowler - - - Sean Brown - - - Alexander Day Chaffee - - - Al Chou - - - Greg Coladonato - - - Maarten Coene - - - Justin Couch - - - Michael Davey - - - Norm Deane - - - Morgan Delagrange - - - Ringo De Smet - - - Russel Dittmar - - - Steve Downey - - - Matthias Eichel - - - Christopher Elkins - - - Chris Feldhacker - - - Roland Foerther - - - Pete Gieser - - - Jason Gritman - - - Matthew Hawthorne - - - Michael Heuer - - - Chas Honton - - - Chris Hyzer - - - Paul Jack - - - Marc Johnson - - - Shaun Kalley - - - Tetsuya Kaneuchi - - - Nissim Karpenstein - - - Ed Korthof - - - Holger Krauth - - - Rafal Krupinski - - - Rafal Krzewski - - - David Leppik - - - Eli Lindsey - - - Sven Ludwig - - - Craig R. McClanahan - - - Rand McNeely - - - Hendrik Maryns - - - Dave Meikle - - - Nikolay Metchev - - - Kasper Nielsen - - - Tim O'Brien - - - Brian S O'Neill - - - Andrew C. Oliver - - - Alban Peignier - - - Moritz Petersen - - - Dmitri Plotnikov - - - Neeme Praks - - - Eric Pugh - - - Stephen Putman - - - Travis Reeder - - - Antony Riley - - - Valentin Rocher - - - Scott Sanders - - - James Sawle - - - Ralph Schaer - - - Henning P. Schmiedehausen - - - Sean Schofield - - - Robert Scholte - - - Reuben Sivan - - - Ville Skytta - - - David M. Sledge - - - Michael A. Smith - - - Jan Sorensen - - - Glen Stampoultzis - - - Scott Stanchfield - - - Jon S. Stevens - - - Sean C. Sullivan - - - Ashwin Suresh - - - Helge Tesgaard - - - Arun Mammen Thomas - - - Masato Tezuka - - - Daniel Trebbien - - - Jeff Varszegi - - - Chris Webb - - - Mario Winterer - - - Stepan Koltsov - - - Holger Hoffstatte - - - Derek C. Ashmore - - - Sebastien Riou - - - Allon Mureinik - - - Adam Hooper - - - Chris Karcher - - - Michael Osipov - - - Thiago Andrade - - - Jonathan Baker - - - Mikhail Mazursky - - - Fabian Lange - - - Michał Kordas - - - Felipe Adorno - - - Adrian Ber - - - Mark Dacek - - - Peter Verhas - - - - - - - - org.junit.jupiter - junit-jupiter - 5.5.2 - test - - - org.junit-pioneer - junit-pioneer - 0.4.2 - test - - - org.hamcrest - hamcrest - 2.2 - test - - - - org.easymock - easymock - 4.1 - test - - - - org.openjdk.jmh - jmh-core - ${jmh.version} - test - - - - org.openjdk.jmh - jmh-generator-annprocess - ${jmh.version} - test - - - - - - - apache.website - Apache Commons Site - scm:svn:https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-lang/ - - - - - -Xmx512m + + -Xmx1G + + + ${heapSize} ${extraArgs} ${systemProperties} ${agentArgs} ISO-8859-1 UTF-8 + + 2025-11-16T12:58:18Z 1.8 1.8 lang lang3 org.apache.commons.lang3 - 3.10 + 3.21.0 + 3.21.1 (Java 8+) 2.6 @@ -593,63 +149,61 @@ commons-lang-${commons.release.2.version} LANG 12310481 - lang https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-lang site-content - utf-8 - - 3.1.0 - 8.27 - - 3.1.12.2 + UTF-8 + src/site/resources/checkstyle false - - - 1.21 benchmarks - - 0.8.4 - 3.0.0-M3 - 3.1.0 - - - false - 0.14.1 - - 3.9 + 3.20.0 RC1 true scm:svn:https://dist.apache.org/repos/dist/dev/commons/lang - Rob Tompkins - B6E73D84EA4FCC47166087253FAAD2CD5ECBB314 + + true + 0.98 + 0.96 + 0.96 + 0.92 + 0.96 + + + 0.91 + 1.15.0 + 2.22.0 + ${commons.conf.dir}/spotbugs-exclude-filter.xml - - - clean verify apache-rat:check clirr:check checkstyle:check spotbugs:check javadoc:javadoc - + clean verify apache-rat:check checkstyle:check japicmp:cmp spotbugs:check pmd:check javadoc:javadoc + - org.apache.rat - apache-rat-plugin + org.apache.maven.plugins + maven-pmd-plugin + ${commons.pmd.version} - - site-content/** - src/site/resources/.htaccess - src/site/resources/download_lang.cgi - src/site/resources/release-notes/RELEASE-NOTES-*.txt - src/test/resources/lang-708-input.txt - + ${maven.compiler.target} + src/conf/pmd-exclude.properties + + src/conf/pmd-ruleset.xml + - - com.github.siom79.japicmp - japicmp-maven-plugin + org.apache.rat + apache-rat-plugin - false + + site-content/** + src/site/resources/.htaccess + src/site/resources/download_lang.cgi + src/site/resources/release-notes/RELEASE-NOTES-*.txt + src/test/resources/lang-708-input.txt + **/*.svg + **/*.xcf + @@ -660,19 +214,29 @@ ${maven.compiler.source} true - utf-8 - utf-8 true - - http://docs.oracle.com/javase/7/docs/api/ - http://docs.oracle.com/javaee/6/api/ - + true true true + all + + + + + org.apache.commons + commons-text + ${commons.text.version} + + + commons-io + commons-io + ${commons.io.version} + + @@ -695,7 +259,7 @@ **/*Test.java - random + false + clean verify apache-rat:check checkstyle:check japicmp:cmp spotbugs:check pmd:check + + java9+ @@ -909,22 +491,66 @@ - -Xmx512m --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED - - true + + --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.time.chrono=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED + + + + maven-javadoc-plugin + + ${maven.compiler.source} + + https://commons.apache.org/proper/commons-io/apidocs + https://commons.apache.org/proper/commons-text/apidocs + ${commons.javadoc.javaee.link} + + + + + + + + java15 + + + 15 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org/apache/commons/lang3/time/Java15BugFastDateParserTest.java + + + + + - java13+ + java-21-up - [13,) + [21,) - - true + + -XX:+EnableDynamicAgentLoading + + + + + java-25-up + + [25,) + + + + 0.98 - benchmark @@ -933,10 +559,20 @@ + + + maven-compiler-plugin + ${commons.compiler.version} + + + **/* + + + + org.codehaus.mojo exec-maven-plugin - 1.6.0 benchmark @@ -964,5 +600,483 @@ + + largeheap + + -Xmx1024m + -Dtest.large.heap=true + + + + + Daniel Rall + dlr + dlr@finemaltcoding.com + CollabNet, Inc. + + Java Developer + + + + Stephen Colebourne + scolebourne + scolebourne@joda.org + SITA ATS Ltd + 0 + + Java Developer + + + + Henri Yandell + bayard + bayard@apache.org + + + Java Developer + + + + Steven Caswell + scaswell + stevencaswell@apache.org + + + Java Developer + + -5 + + + Robert Burrell Donkin + rdonkin + rdonkin@apache.org + + + Java Developer + + + + ggregory + Gary Gregory + ggregory at apache.org + https://www.garygregory.com + The Apache Software Foundation + https://www.apache.org/ + + PMC Member + + America/New_York + + https://people.apache.org/~ggregory/img/garydgregory80.png + + + + Fredrik Westermarck + fredrik + + + + Java Developer + + + + James Carman + jcarman + jcarman@apache.org + Carman Consulting, Inc. + + Java Developer + + + + Niall Pemberton + niallp + + Java Developer + + + + Matt Benson + mbenson + + Java Developer + + + + Joerg Schaible + joehni + joerg.schaible@gmx.de + + Java Developer + + +1 + + + Oliver Heger + oheger + oheger@apache.org + +1 + + Java Developer + + + + Paul Benedict + pbenedict + pbenedict@apache.org + + Java Developer + + + + Benedikt Ritter + britter + britter@apache.org + + Java Developer + + + + Duncan Jones + djones + djones@apache.org + 0 + + Java Developer + + + + Loic Guibert + lguibert + lguibert@apache.org + +4 + + Java Developer + + + + Rob Tompkins + chtompki + chtompki@apache.org + -5 + + Java Developer + + + + + + C. Scott Ananian + + + Chris Audley + + + Stephane Bailliez + + + Michael Becke + + + Benjamin Bentmann + + + Ola Berg + + + Nathan Beyer + + + Stefan Bodewig + + + Janek Bogucki + + + Mike Bowler + + + Sean Brown + + + Alexander Day Chaffee + + + Al Chou + + + Greg Coladonato + + + Maarten Coene + + + Justin Couch + + + Michael Davey + + + Norm Deane + + + Morgan Delagrange + + + Ringo De Smet + + + Russel Dittmar + + + Steve Downey + + + Matthias Eichel + + + Christopher Elkins + + + Chris Feldhacker + + + Roland Foerther + + + Pete Gieser + + + Jason Gritman + + + Matthew Hawthorne + + + Michael Heuer + + + Chas Honton + + + Chris Hyzer + + + Paul Jack + + + Marc Johnson + + + Shaun Kalley + + + Tetsuya Kaneuchi + + + Nissim Karpenstein + + + Ed Korthof + + + Holger Krauth + + + Rafal Krupinski + + + Rafal Krzewski + + + David Leppik + + + Eli Lindsey + + + Sven Ludwig + + + Craig R. McClanahan + + + Rand McNeely + + + Hendrik Maryns + + + Dave Meikle + + + Nikolay Metchev + + + Kasper Nielsen + + + Tim O'Brien + + + Brian S O'Neill + + + Andrew C. Oliver + + + Alban Peignier + + + Moritz Petersen + + + Dmitri Plotnikov + + + Neeme Praks + + + Eric Pugh + + + Stephen Putman + + + Travis Reeder + + + Antony Riley + + + Valentin Rocher + + + Scott Sanders + + + James Sawle + + + Ralph Schaer + + + Henning P. Schmiedehausen + + + Sean Schofield + + + Robert Scholte + + + Reuben Sivan + + + Ville Skytta + + + David M. Sledge + + + Michael A. Smith + + + Jan Sorensen + + + Glen Stampoultzis + + + Scott Stanchfield + + + Jon S. Stevens + + + Sean C. Sullivan + + + Ashwin Suresh + + + Helge Tesgaard + + + Arun Mammen Thomas + + + Masato Tezuka + + + Daniel Trebbien + + + Jeff Varszegi + + + Chris Webb + + + Mario Winterer + + + Stepan Koltsov + + + Holger Hoffstatte + + + Derek C. Ashmore + + + Sebastien Riou + + + Allon Mureinik + + + Adam Hooper + + + Chris Karcher + + + Michael Osipov + + + Thiago Andrade + + + Jonathan Baker + + + Mikhail Mazursky + + + Fabian Lange + + + Michał Kordas + + + Felipe Adorno + + + Adrian Ber + + + Mark Dacek + + + Peter Verhas + + + Jin Xu + + + Arturo Bernal + + diff --git a/src/assembly/bin.xml b/src/assembly/bin.xml index fb1e07c9a84..1d8fd1b9024 100644 --- a/src/assembly/bin.xml +++ b/src/assembly/bin.xml @@ -6,7 +6,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - + bin tar.gz @@ -37,6 +39,7 @@ *.jar + 644 target/site/apidocs diff --git a/src/assembly/src.xml b/src/assembly/src.xml index 1a9ad19f3ac..e5750a92593 100644 --- a/src/assembly/src.xml +++ b/src/assembly/src.xml @@ -6,7 +6,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - + src tar.gz diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c5516d5f222..a6770692a27 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -7,7 +7,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ This file is also used by the maven-changes-plugin to generate the release notes. Useful ways of finding items to add to this file are: -1. Add items when you fix a bug or add a feature (this makes the +1. Add items when you fix a bug or add a feature (this makes the release process easy :-). 2. Do a JIRA search for tickets closed since the previous release. @@ -32,25 +32,1049 @@ To generate the release notes from this file: mvn changes:announcement-generate -Prelease-notes [-Dchanges.version=nnn] -then tweak the formatting if necessary +then tweak the formatting if necessary and commit The type attribute can be add,update,fix,remove. --> - - - + Apache Commons Lang Release Notes + + + Stop ExtendedMessageFormat seekNonWs reading past the pattern end. + Fix spelling and grammar in StringUtils #1486. + Add ConversionTest assertions to increase coverage #1489. + Use Class<?> instead of Class<? extends Object> in MethodUtils #1491. + ClassUtils now throws IllegalArgumentException if a class name represents an array with more dimensions than the JVM supports, 255: + getClass(ClassLoader, String, boolean), + ClassUtils.getClass(ClassLoader, String), + ClassUtils.getClass(String, boolean), + ClassUtils.getClass(String), + #1494. + ClassUtils now throws IllegalArgumentException if a class name length is greater than 65,535: + getClass(ClassLoader, String, boolean), + ClassUtils.getClass(ClassLoader, String), + ClassUtils.getClass(String, boolean), + ClassUtils.getClass(String), + #1495. + Fix console compiler warning: TypeUtils.java:[361,33] non-varargs call of varargs method with inexact argument type for last parameter. + Fix console compiler warning: ReflectionDiffBuilderTest.java:[172,52] non-varargs call of varargs method with inexact argument type for last parameter. + Fix Apache RAT plugin console warnings. + Fix Javadoc description in CharUtils.isAsciiAlphanumeric() #1501. + Fix Javadoc for RandomUtils.secure(), it incorrectly mentions securerandom.strongAlgorithms #1503. + Fix NullPointerException in ReflectionDiffBuilder.getExcludeFieldNames() when instance created with ReflectionDiffBuilder.ReflectionDiffBuilder(T, T, ToStringStyle). + Fail-fast for a null DiffBuilder in ReflectionDiffBuilder.ReflectionDiffBuilder(DiffBuilder, String[]) instead of getting a NullPointerException in ReflectionDiffBuilder instance methods. + Fix NumberUtilsTest#testMaxDouble() and testMaxFloat() #4959. + Fix Javadoc for IEEE754rUtils.max(float...) and IEEE754rUtils.max(double...). + Fix StringUtilsSubstringTest.testCountMatches_char() to call the correct method. + Make private static variables final in RandomStringUtils #1513. + Speed up StringUtils.getDigits(String) #1515. + Remove redundant length check in StringUtils.isBlank(CharSequence) #1516. + Incorrect grammar and unclear wording in RandomStringUtils#random method #1520. + Fix collision in CharRange.hashCode(). + Fix race condition in Fraction.hashCode(). + Fix race condition in Range.hashCode(). + Document safer deserialization option in Javadoc for SerializationUtils. + NumberUtils.isParsable(String) returns true for full width Unicode digits for inputs with a decimal point. + Fix incorrect method invocation in ObjectUtilsTest and Javadoc reference in RandomStringUtils. + Allow trailing decimal point in NumberUtils.isParsable(String) #1531. + Fix CharSet#getInstance returns null instead of EMPTY when input setStrs is null #1530. + Speedup CharSequenceUtils.toCharArray(CharSequence) for StringBuilder input: 160-205% improvement (2-3x faster), see CharSequenceUtilsBenchmark. + Speedup CharSequenceUtils.toCharArray(CharSequence) for StringBuffer input: 300-4,250% improvement (4-44x faster), see CharSequenceUtilsBenchmark. + Speedup CharSequenceUtils.toCharArray(CharSequence) for String input: ~1-2% improvement (essentially identical). + Streamline StringUtils.truncate(String, int, int). + Fix Javadoc comma spacing #1538. + Make private static variables final in RandomUtils. + Javadoc: Normalize some varargs examples. + Speedup StringUtils.join(AllPrimitiveTypes[], char, int, int), see StringUtilsJoinBenchmark, based on #1532. Also better StringBuilder allocation + TypeUtils.isAssignable returns a wrong result for ParameterizedType when raw class is "Class" #1548. + Improve handling of parameterized types and variable unrolling. + ArrayUtils.shuffle() throws NullPointerException for null array input. + [javadoc] Fix incorrect exception type in CharUtils Javadoc examples #1563. + [javadoc] Fix incorrect class names in RegExUtils Javadoc examples #1564. + NumberUtils.isParsable("1.f") should return true #1560. + Simplify literal assignments in ArrayFillTest #1567. + Make ArrayUtils methods implementation consistent with other overloads #1568. + Fix handling of null marker in StringUtils.abbreviate(String, String, int, int) #1571. + Better exception messages from FastDateParser.parse(String). + Simplify NumberUtils.isParsable(String) #1570. + RandomStringUtils.random() methods may not return when asked to generate only letters or only numbers while provided with a range that contains neither. + Fix Javadoc parameter comments in StringUtils.truncate() methods #1536. + Improve test coverage for TypeUtils.isAssignable() #1574. + Improve branch coverage for Processor (isAarch64, isRISCV); add ProcessorTest #1578. + Fix ClassNotFoundException message in ClassUtils.getClass(String) #1577. + Fix Javadoc in DoubleRange.of(Double, Double) to reflect actual exception type thrown #1581. + RecursiveToStringStyle and MultilineRecursiveToStringStyle shouldn't recurse into a java.math.BigDecimal #1584. + ArrayUtils.subarray(..) may overflow index arithmetic and violate contract for extreme index values. + ArrayUtils contains/indexOf/indexesOf with tolerance fail to match NaN values #1589. + Fix StringIndexOutOfBoundsException message in StrBuilder.append(char[], int, int). + Fix ClassUtils.getShortClassName(Class) to correctly handle $ in valid class names #1591. + ThreadUtils.sleepQuietly(Duration) now restores the current thread's interrupt flag when catching InterruptedException. + UncheckedFutureImpl clears thread interrupt status when wrapping InterruptedException #1590. + Fix StringUtils.abbreviate(String, String, int) contract violations #1572. + Add test coverage for negative-day adjustment in DurationFormatUtils #1596. + [Javadoc] Add usage examples to CharSet.contains(char) #1605. + CharSet now maintains iteration order. + DateUtils.ceiling does not behave correctly for dates on the boundaries. + [LANG-] Javadoc ArrayUtils.removeAll() null input behavior #1614. + ArrayUtils#hashCode() Javadoc incorrectly states “null returns zero” (actual result is 629). + Deprecate Validate.Validate(). + [javadoc] Fix ObjectUtils.anyNull(Object) for empty arrays. + Fix StopWatch's stopInstant match stopTimeNanos when split is called #1610. + Fix NullPointerException in StopWatch.getStopTime(). + Add "ppc64le" to ArchUtils #1625. See https://bugs.gentoo.org/972590 +Bug 972590 - dev-java/commons-lang-3.20.0: +java.lang.NullPointerException: Cannot invoke +"org.apache.commons.lang3.arch.Processor.toString()" because "<local1>" + NumberUtils.isCreatable(String) should match NumberUtils.createNumber(String), exactly #1626. + NumberUtils.isCreatable fails for hexadecimal numbers with long type qualifier. + NumberUtils.createNumber() performance: Short-circuit check based on pre-derived candidate Double #1628. + LocaleUtils.toLocale cannot parse valid JDK Locale string containing '#' #1630. + LocaleUtils test fails with new Locale "ja_JP_JP_#u-ca-japanese" of JDK7. + Fix typo in SystemProperties.JDK_XML_ENTITY_REPLACEMENT_LIMIT (#1631). + Harden Range.readObject() to reject bad cached hash code (#1633). + Harden Fraction.readObject() to reject bad cached hash code (#1634). + NumberUtils.createNumber(String): Float shortcut can bypass exact decimal parsing (#1635). + StringUtils.joins() for primitive types can throw OOME before index check (#1636). + Annotation lookup in MethodUtils.getAnnotation(Method, Class, boolean, boolean) matches unrelated overloads via assignable-parameter resolution (#1637). + RandomStringUtils.random(...): A custom chars array throws IllegalArgumentException because validation loops treat the loop index as a char code point instead of an index into the chars array (#1638). + RandomStringUtils.random(...): Can hang when the specified [start, end) range contains ONLY rejected code points (UNASSIGNED, PRIVATE_USE, SURROGATE). The loop increments count and retries indefinitely (#1638). + TimedSemaphore.shutdown() must wake threads blocked in acquire() (#1639). + NaN bypass in primitive double range validators (#1640). + StringUtils.repeat(String, [String,] int) now throws IllegalArgumentException instead of NegativeArraySizeException (#1644). + EqualsBuilder.reflectionAppend ttries to set visibility on excluded fields #1647. + ToStringStyle.appendDetail(StringBuffer,String,Collection) bypass cycle registry. #1648. + ToStringStyle.appendDetail(StringBuffer,String,Map) bypass cycle registry. #1648. + WordUtils.wrap() zero-width wrapOn regex match causes infinite loop (#1649).WordUtils.wrap() zero-width wrapOn regex match causes infinite loop (#1649). + HashCodeBuilder.append(Object) StackOverflowError on mutually-referential objects (#1650). + AtomicSafeInitializer.get() busy-spin without yield burns CPU during slow initialization (#1651). + StrBuilder.readFrom(Readable) exposes stale internal buffer to Readable parameter (#1652). + EqualsBuilder.reflectionEquals() array branch missing cycle guard causes stack overflow on self-referential Object arrays (#1653). + WordUtils.wrap() leaves separator characters in the output when the wrapOn regex match is longer than one character (#1655). + StringUtils.indexOfAny(CharSequence, int, char...) should not throw for a bad index (#1656). + StrBuilder.deleteImpl(int, int, int) doesn't clear its unused bytes. (#1654). + LocaleUtils.parseLocale(String) operator precedence bug allows invalid language with numeric country (#1658). + DurationUtils.toMillisInt() throws ArithmeticException on long overflows before clamping (#1657). + FastDatePrinter WeekYear rule calls Calendar.getWeekYear() may throw UnsupportedOperationException (#1659). + DateUtils.parseDateWithLeniency() doesn't reset its time zone (#1660). + FormattableUtils.append re-parses literal output as format string (#1661). + ClassUtils.getShortClassName(String) can throw NoClassDefFoundError on malformed inner classes (#1665). + TimeZones.GMT is a mutable public static final field (#1666). + CompareToBuilder lacks cycle guard on Object[] recursion. (#1669). + ClassUtils.toCleanName(String) resolves malformed array suffixes (#1670). + StrBuilder.setLength(int) shrink-branch leaves residual chars in buffer tail (#1671). + StrBuilder.replaceImpl() shrink-branch leaves residual chars in buffer tail (#1672). + NumericEntityUnescaper.translate() throws IllegalArgumentException for values out of range (#1673). + FastTimeZone.getGmtTimeZone() Javadoc promises null on no-match; regex always matches (#1674). + Range.readObject() does not null-check comparator, minimum, or maximum (#1675). + Make the StopWatch more reliable for tracking nanos, adjust suspend test (#1677). + RandomStringUtils.random() does not strictly validate start and end (#1682). + Fix CharSequenceUtils.lastIndexOf for supplementary code points #1684. + Range.readObject() does not re-assert the comparator/ordering invariant (#1686). + Fraction.readObject() does not re-assert denominator != 0 (#1688). + FastDateParser.readObject(ObjectInputStream) now validates the same (#1692). + Fix StringUtils.indexOfAny() matching an unpaired trailing high surrogate (#1687). + Fix LookupTranslator code point consumption for supplementary keys (#1691). + Re-assert CharRange start <= end invariant in readObject (#1693). + FluentBitSet.readObject(ObjectInputStream) should validate constructor invariants (#1694). + EventListenerSupport.readObject(ObjectInputStream) should validate constructor invariants (#1695). + Correct JavaDoc in Strings.java concerning case-sensitivity (#1696). + Fix case-insensitive Strings.replace() dropping length-changing matches (#1697). + Fix int overflow in StringUtils.midString() and StrBuilder.midString() length handling (#1699). + Throw IllegalArgumentException for trailing whitespace in ExtendedMessageFormat argument index (#1701). + Reject doubled leading sign in NumberUtils.createBigInteger (#1702). + Fix RandomStringUtils.random() false rejection of letters and digits (#1703). + Fix substringAfter(Last) for supplementary code points (#1707). + Strip L suffix for oversized hex literals in createNumber (#1712). + Fix spurious overflow in Fraction.add/subtract for coprime denominators (#1709). + Drop trailing empty token from splitByWholeSeparator (#1710). + Fix indexOfDifference splitting a surrogate pair (#1713). + Fix int mask sign extension in BitField long methods (#1711). + Account for duplicate values in ObjectUtils.median() (#1715). + Fix silent int overflow in Fraction.getFraction(double) (#1717). + Find NaN in ArrayUtils.lastIndexOf for double and float arrays (#1718). + Reject sign characters in UnicodeUnescaper hex values (#1721). + Keep abbreviate and truncate off surrogate pair boundaries (#1719). + Keep initials from splitting a supplementary code point (#1722). + Fix int overflow in DurationFormatUtils.formatPeriod (#1720). + Fix CsvUnescaper bounds error on a single quote field (#1723). + Fix Strings.replace overflow on large replacements (#1716). + Classify supplementary code points in StringUtils is* predicates (#1724). + Emit surrogate pair for supplementary code points in UnicodeEscaper (#1726). + Fix MethodUtils.getMatchingMethod false ambiguity on boxed arguments (#1727). + Fold supplementary code points in CharSequenceUtils.regionMatches (#1725). + Fix WordUtils.containsAllWords missing words across line breaks (#1732). + Handle supplementary code points in WordUtils case methods (#1728). + Fix StringUtils.getDigits dropping supplementary digits (#1729). + Keep StrBuilder.reverse from splitting surrogate pairs (#1730). + Fix NumberUtils min/max varargs dropping the sign of zero (#1733). + Handle supplementary code points in StringUtils.splitByCharacterType() (#1734). + Fix OOM in StringUtils.leftPad/rightPad when size is Integer.MIN_VALUE (#1736). + Fix Strings.CI.indexOf() returning out-of-range index for empty search (#1737). + Support escaped single quotes ('') in DurationFormatUtils format patterns (#1700). + Keep DurationFormatUtils.lexx() from merging repeated fields split by a literal (#1738). + Reject hex letters in NumericEntityUnescaper decimal scan (#1739). + Keep FastDateParser numeric parse from throwing on int overflow (#1741). + Keep FastDateParser.parse error message intact for Japanese imperial locale (#1743). + Keep ExtendedMessageFormat from rejecting a quoted format style (#1744). + Match String case folding in StrBuilder.equalsIgnoreCase (#1745). + Fix FieldUtils.getField false ambiguity on inherited interface field (#1748). + Skip Chuvash locale parser tests on Java 27 (#1746). + Fix ArrayUtils.reverse range underflow on Integer.MIN_VALUE end (#1750). + Fix sign extension in BitField.getValue for top-bit fields (#1749). + Fix int overflow in FluentBitSet.setInclusive at Integer.MAX_VALUE (#1751). + Add messages when throwing NullPointerException. + [Javadoc] Fix Failable class comment to clarify comparison of lambda versions (#1752). + Add tests for DateUtils.truncatedCompareTo and truncatedEquals (#1753). + Avoid repeated regex compilation in ClassUtils.toCleanName() (#1754). + Add missing test for ObjectUtils.isEmpty(Object) and wait(Object, Duration) (#1755, #1756). + Fix negative week year formatting in FastDatePrinter (#1762). + Keep abbreviateMiddle off surrogate pair boundaries (#1758). + Fix AnnotationUtils.equals for package-private annotations (#1760). + Fix two Javadoc errors in StringUtils (#1763). + Reject non-ASCII hex characters in Conversion.hexDigitToInt (#1767). + Fix the overflow bound direction in Instants.toMillisSince(Instant) (#1766). + Fix spurious overflow in Fraction.multiplyBy for unreduced operands (#1769). + Fix low surrogate false match in StringUtils containsAny/containsNone/indexOfAny (#1771). + Keep StringUtils.chop from splitting a trailing surrogate pair (#1770). + Keep StringUtils stripStart and stripEnd off surrogate pair boundaries (#1773). + Fix the month/day example in DurationFormatUtils.formatPeriod Javadoc (#1772). + Fix typos in Javadoc and example comments (#1774). + Fix CharRange.contains(CharRange) for negated argument ranges (#1775). + Fix SpotBugs USO_UNSAFE_METHOD_SYNCHRONIZATION in CharSet. + Keep StopWatch.formatSplitTime from clamping splits to int millis (#1777). + Keep StringUtils left, right, mid, and overlay off surrogate pair boundaries (#1776). + Align ReflectionDiffBuilder with AbstractReflection and add cycle detection to prevent StackOverflowError on cyclic object graphs. + Fix DurationFormatUtils.formatPeriod() calculation when pattern omits 'M' (#1780). + FastDateParser parses 'Y' (week year) as plain calendar year; asymmetric with FastDatePrinter and with SimpleDateFormat; boundary dates shift by a full year, silently (f002). + UnicodeUnescaper.unescapeJava/EcmaScript/Json: malformed \u sequences throw undeclared IllegalArgumentException, AND non-ASCII digit spellings of \u escapes are silently accepted; both arms of one missing ASCII-hex prescan (f004). + StringUtils.replaceEachRepeatedly derives its recursion budget from the input itself; the documented StackOverflowError protection fails on large tables, and expanding rules amplify text 64x even at the default TTL (f005). + EventUtils.EventBindingInvocationHandler.invoke() dispatches Object.hashCode/equals/toString into the bound business method (empty-eventTypes default), or returns null -> NPE from hash collections (non-empty) (f006). + LocaleUtils static caches no longer grows on invalid input to LocaleUtils.countriesByLanguage(String) (f007). + LocaleUtils static caches no longer grows on invalid input to LocaleUtils.countriesByLanguage(String) (f007). + ExtendedMessageFormat.applyPattern() is quadratic: full pattern.toCharArray() per token (f008). + WordUtils.wrap(wrapLongWords=false, the 2-arg default) copies the entire remaining string every iteration. (f009). + FastDateParser.parse throws undeclared IllegalArgumentException, NullPointerException, and IllegalStateException on crafted date strings (f010). + Memoizer: default caches the first failure forever, has no size bound or eviction, and runs the user computation inside the ConcurrentHashMap bin lock (blocking unrelated keys, deadlocking reentrant use) (f011). + StringEscapeUtils.escapeEcmaScript() misses backtick/template-literal (`, ${) and inline-script parser-state sequences (<!--, <script) - claim 'Deals correctly with quotes' is falsified by ES6 (f012). + StrSubstitutor (deprecated) recursive expansion has a cycle check but no fan-out, depth, or size bound (f013). + DurationFormatUtils.formatPeriod walks the calendar one year per iteration, Long.MAX_VALUE endMillis forces ~292 million Calendar round trips on one thread (f014). + JavaVersion.get() throws NumberFormatException where javadoc promises null (f015). + Match Javadoc to code in SystemProperties.getProperty() and SystemUtils.getEnvironmentVariable() and update inline comments (f016). + Fix LockingVisitors Javadoc to match code (f017). + DoubleRange/Range accept NaN endpoints, silently producing a half-unbounded range, contains()/fit() fail open for every value above the minimum (f018). + BitField long-mask fields silently mis-answer int/short/byte accessors: shifts masked mod 32, and sign extension makes above-bit-31 flags read as set on any negative holder (f019). + ClassUtils.getClass() name normalization (whitespace stripping + dot-to-dollar rewriting) defeats host-side class-name string checks (f020). + Fraction.getFraction(String) throws undeclared ArithmeticException on crafted numeric strings ('9999999999.5', '1/0', mixed-number overflow) (f021). + StringEscapeUtils.escapeHtml4/3 apostrophe gap: single-quoted and unquoted HTML attribute contexts are trivially breakable, and the javadoc discloses the gap only as an HTML4 entity-legality footnote (f022). + ArrayUtils.addAll/insert length arithmetic overflows (undeclared NegativeArraySizeException) while sibling concat is overflow-checked (f023). + FormattableUtils.append pads right-justified output with insert(0) per char; O(width^2); '%500000s' costs ~1.25e11 char moves (f024). + Attacker-controlled exception message forges frames in getRootCauseStackTrace output AND suppresses all real frames, beyond cosmetic log spoofing (f025). + StrBuilder.indexOf materializes the whole builder as a String per call; deleteAll/replaceAll multiply it into ~750 GB churn on a 1 MB builder (f026). + ThresholdCircuitBreaker accepts negative increments and overflows its accumulator, silently keeping the breaker closed (f027). + ExceptionUtils.getThrowableList() cycle check is O(n^2) over deep cause chains; all chain-walking consumers inherit it (f031). + Reflection builders' cycle registry unregisters per-visit: cycle-SAFE but DAG-exponential, a 40-level reference diamond drives ~2^40 traversals and unbounded output (f032). + FastDateFormat static caches grow without bound per distinct pattern/zone/locale, untrusted patterns pin several KB each, forever (f033). + Fix Fraction.add and subtract for operands not in lowest terms (#1784). + Fix MethodUtils.invokeMethod on instances of non-public classes (#1783). + Fraction.add and subtract return the reduced form when an operand is zero (#1787). + + Add JavaVersion.JAVA_27. + Add SystemUtils.IS_JAVA_27. + JavaVersion.get(String) now support Java 26 and 27. + Deprecate ArrayUtils.SOFT_MAX_ARRAY_LENGTH in favor of SAFE_MAX_ARRAY_LENGTH #1559. + Add long support to BitField #1561. + Add ArrayUtils.getDimensions(Object). + Add ArrayUtils.concat() methods for concatenating multiple arrays #1519. + Add FailableConsumer.accept(FailableConsumer, T) (#1641). + Add AutoCloseables #1643. + Make object accessibility optional with AbstractReflection.AbstractBuilder.setForceAccessible(boolean) (#1558). + Add ArrayFill.clear(char[]) (#1667). + Add DurationUtils.toMillisLong(Duration) (#1679). + Add Instants.toEpochMillis(Instant) (#1680). + Add StringUtils.trimAsciiControl(String) (#1681). + Add Instants.toMillisSince(Instant) (#1683). + Add CharUtils isHex(int), isAsciiNumeric(int), isOctal(int) (#1685). + Add and use Instants.toInstant(Instant[, Instant]) (#1690). + Handle ParsePosition index beyond source string length cleanly (#1757). + Fix StrBuilder.lastIndexOf(String) start index for an empty search (#1759). + Fix int overflow in Conversion count and position bounds guards (#1779). + Add ClassUtils.getClassStrict(ClassLoader, String, boolean) (f020). + Add ClassUtils.getClassStrict(String) (f020). + Fix TypeUtils.isAssignable() for wildcards with multiple upper bounds (#1782). + + Bump org.apache.commons:commons-parent from 92 to 105 #1498. + [test] Bump org.apache.commons:commons-text from 1.14.0 to 1.15.0. + + + + Optimize ObjectToStringComparator.compare() method #1449. + [javadoc] Improve StringUtils Javadoc #1450. + Fix internal inverted logic in private isEnum() method and correct its usage in getFirstEnum() #1454. + Use accessors in ToStringStyle so subclasses can effectively override them. + `LocaleUtils.toLocale(String)` for a 2 letter country code now returns a value instead of throwing an `IllegalArgumentException`. + Fix typo in StringUtils.truncate() IllegalArgumentException message and test assertion messages. + Fix test fixture in ReflectionDiffBuilderTest.testTransientFieldDifference() #1464. + NullPointerException when generating NoSuchMethodException in MethodUtils. + Map deprecated TimeZone short IDs and avoid JRE WARNINGs to the console #1483. + TypeUtils.toString() skips angle brackets for Class type. + Mention JDK 25 LTS as a tested version in the release notes #1485. + + Add SystemProperties.getPath(String, Supplier<Path>). + Add JavaVersion.JAVA_25. + Add JavaVersion.JAVA_26. + Add SystemUtils.IS_JAVA_25. + Add SystemUtils.IS_JAVA_26. + Add MutablePair.ofNonNull(Map.Entry). + Add TimedSemaphore.builder(), Builder, and deprecate constructors. + Adding labels and history to split StopWatch #1473. + + Bump org.apache.commons:commons-parent from 88 to 92 #1472, #1484. + [test] Bump commons-io:commons-io from 2.21.0 to 2.22.0. + + + + MethodUtils.getMatchingMethod() doesn't respect the hierarchy of methods #1414. + MethodUtils.getMethodObject(Class<?>, String, Class<?>...) now returns null instead of throwing a NullPointerException, as it does for other exception types. + Reduce spurious failures in ArrayUtilsTest methods that test ArrayUtils.shuffle() methods. + MethodUtils cannot find or invoke a public method on a public class implemented in its package-private superclass. + AtomicSafeInitializer.get() can spin internally if the FailableSupplier given to AbstractConcurrentInitializer.AbstractBuilder.setInitializer(FailableSupplier) throws a RuntimeException. + WordUtils.containsAllWords​() may throw PatternSyntaxException. + MethodUtils cannot find or invoke vararg methods without providing vararg types or values #1427. + MethodUtils cannot find or invoke vararg methods of interface types. + MethodUtils cannot find or invoke vararg methods when widening primitive types following the JLS 5.1.2. Widening Primitive Conversion. + Invocation fails because matching varargs method found but then discarded. + Don't check accessibility twice in MemberUtils.setAccessibleWorkaround(T). + Improve handling of ClassUtils.getShortCanonicalName() for invalid input #1437. + Improve Javadocs for Conversion. + Fix CalendarUtils.toLocalDate() Javadoc return type description #1440. + Fix the method name in Javadoc examples for CharUtils.isHex() #1444. + Deprecate NumberUtils.compare(byte, byte) in favor of Byte.compare(byte, byte). + Deprecate NumberUtils.compare(int, int) in favor of Integer.compare(int, int). + Deprecate NumberUtils.compare(long, long) in favor of Long.compare(long, long). + Deprecate NumberUtils.compare(short, short) in favor of Short.compare(short, short). + Deprecate obsolete system property constant SystemProperties.AWT_TOOLKIT. + Deprecate obsolete system property constant SystemProperties.JAVA_AWT_FONTS. + Deprecate obsolete system property constant SystemProperties.JAVA_AWT_GRAPHICSENV. + Deprecate obsolete system property constant SystemProperties.JAVA_AWT_HEADLESS. + Deprecate obsolete system property constant SystemProperties.JAVA_AWT_PRINTERJOB. + Deprecate obsolete system property constant SystemProperties.JAVA_COMPILER. + Deprecate obsolete system property constant SystemProperties.JAVA_ENDORSED_DIRS. + Deprecate obsolete system property constant SystemProperties.JAVA_EXT_DIRS. + Deprecate method for obsolete system property constant SystemProperties.getAwtToolkit() + Deprecate method for obsolete system property constant SystemProperties.getJavaAwtFonts() + Deprecate method for obsolete system property constant SystemProperties.getJavaAwtGraphicsenv() + Deprecate method for obsolete system property constant SystemProperties.getJavaAwtHeadless() + Deprecate method for obsolete system property constant SystemProperties.getJavaAwtPrinterjob() + Deprecate method for obsolete system property constant SystemProperties.getJavaCompiler() + Deprecate method for obsolete system property constant SystemProperties.getJavaEndorsedDirs() + Deprecate method for obsolete system property constant SystemProperties.getJavaExtDirs() + Deprecate method for obsolete system property constant SystemUtils.isJavaAwtHeadless() + Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_FONTS. + Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_GRAPHICSENV. + Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_HEADLESS. + Deprecate constants for obsolete system property SystemUtils.JAVA_AWT_PRINTERJOB. + Deprecate constants for obsolete system property SystemUtils.JAVA_COMPILER. + Deprecate constants for obsolete system property SystemUtils.JAVA_ENDORSED_DIRS. + Deprecate constants for obsolete system property SystemUtils.JAVA_EXT_DIRS. + [javadoc] General improvements. + [javadoc] Fix thrown exception documentation for MethodUtils.getMethodObject(Class<?>, String, Class<?>...). + [javadoc] Strings::equalsAny: CI doc string should show it's insensitive #1416. + [javadoc] General Javadoc improvements. + [javadoc] Fix Strings Javadoc #1419. + [javadoc] Fix typo in Javadoc of Strings instances #1406. + [javadoc] Fix Javadocs in ClassUtils #1410. + [javadoc] Fix @deprecated link for StringUtils#startsWithAny #1424. + Replace old feather logotype with new oak logotype. + + Add ArrayUtils.SOFT_MAX_ARRAY_LENGTH. + Add SystemUtils.IS_OS_NETWARE. + Add MethodUtils.getAccessibleMethod(Class, Method). + Add documentation to site for CVE-2025-48924 ClassUtils.getClass(...) can throw a StackOverflowError on very long inputs. + Add StringUtils.indexOfAny(CharSequence, int, char...). + Add ConcurrentException.ConcurrentException(String). + Add DateUtils.toLocalDateTime(Date[, TimeZone]) #1385. + Add DateUtils.toOffsetDateTime(Date[, TimeZone]). + Add DateUtils.toZonedDateTime(Date[, TimeZone]). + Add ByteConsumer. + Add ByteSupplier. + Add FailableByteConsumer. + Add FailableByteSupplier. + Add Functions methods for null-safe mapping and chaining #1435. + Add Failable methods for null-safe mapping and chaining #1435. + Add DoubleRange.fit(double). + Add IntegerRange.fit(int). + Add LongRange.fit(long). + Add DurationUtils.get(String, TemporalUnit, long). + Add DurationUtils.getMillis(String, long). + Add DurationUtils.getSeconds(String, long). + Add SystemProperties.getBoolean(Class, String, boolean). + Add SystemProperties.getInt(Class, String, int). + Add SystemProperties.getLong(Class, String, long). + + [test] Bump org.apache.commons:commons-text from 1.13.1 to 1.14.0. + Bump org.apache.commons:commons-parent from 85 to 88. + + + + Fix flaky FileUtilsWaitForTest.testWaitForNegativeDuration(). + Pick up exec-maven-plugin version from parent POM. + Speed up and sanitize StopWatchTest. + Fix handling of non-ASCII letters and numbers in RandomStringUtils #1273. + Rewrite ClassUtils.getClass(...) without recursion to avoid StackOverflowError on very long inputs. + OSS-Fuzz Issue 42522972: apache-commons-text:StringSubstitutorInterpolatorFuzzer: Security exception in ClassUtils.getClass. + Remove trailing whitespace in StopWatch exception messages. + Use getAllSuperclassesAndInterfaces() in getMatchingMethod() #1289. + Add details to the ArrayFill Javadoc. + Add details to the ArraySorter Javadoc. + Fix broken URL to project location in Maven Central #1296. + StringUtils.replaceEachRepeatedly regression in 3.11+ #1297. + Use simplified JUnit assertion methods #1298. + Javadoc and test: Use Strings.CI.startsWithAny method instead #1299. + Fix NullPointerException in FastDateParser.TimeZoneStrategy.setCalendar(FastDateParser, Calendar, String) on Java 23. + Fix NullPointerException in MethodUtils.getMatchingAccessibleMethod((Class, String, Class...)). + Fix StackOverflowError in TypeUtils.typeVariableToString(TypeVariable), TypeUtils.toString(Type) on Java 17 and up. + SystemUtils is missing important documentation. + Make Failable.run(FailableRunnable) null-safe. + Make Failable.accept(*) null-safe. + Improve container detection by mimicking systemd #1323. + Make LangCollectors.collect(...) null-safe. + Make LangCollectors.collect(...) null-safe. + Fix names of UTF-16 surrogate character test fixture constants, see also #1326. + Moditect plugin generates split package warnings. + LocaleUtils.availableLocaleSet() uses predictable iteration order. + SerializationUtils.clone(Object) throws ClassCastException when called with a Serializable lambda. + [StringUtils::indexOfAnyBut] redesign due to inconsistent/faulty behavior regarding UTF-16 surrogates #1327. + Undeprecate ObjectUtils.toString(Object). + Fix Spotbugs [ERROR] Medium: The field DiffBuilder$SDiff.leftSupplier is transient but isn't set by deserialization [DiffBuilder$SDiff] In DiffBuilder.java SE_TRANSIENT_FIELD_NOT_RESTORED. + Fix Spotbugs [ERROR] Medium: The field DiffBuilder$SDiff.rightSupplier is transient but isn't set by deserialization [DiffBuilder$SDiff] In DiffBuilder.java SE_TRANSIENT_FIELD_NOT_RESTORED. + StopWatch methods should not delegate to deprecated methods. + Don't call TypeUtils.toString(Type) on every array item in TypeUtils.parameterize[WithOwner](Type, Class, Map, Type>) unless required. + Remove -nouses directive from maven-bundle-plugin. OSGi package imports now state 'uses' definitions for package imports, this doesn't affect JPMS (from org.apache.commons:commons-parent:80). + Instead of throwing a NullPointerException, ArrayUtils.toStringArray(Object[]) should return "null" for null elements like ArrayUtils.toStringArray(Object[], String) returns its valueForNullElements. + Deprecate NumericEntityUnescaper.OPTION in favor of Apache Commons Text. + Several hash collisions in Fraction class. + MutableLong and friends should provide better parsing exceptions Javadocs. + Reimplement StringUtils.toCodePoints(CharSequence) to use java.lang.CharSequence.codePoints(). + Reimplement StringUtils.capitalize(String) to use java.lang.CharSequence.codePoints(). + Reimplement StringUtils.uncapitalize(String) to use java.lang.CharSequence.codePoints(). + ClassUtils.getCanonicalName(String) now throws an IllegalArgumentException for array dimensions greater than 255. + Fix Javadoc typo and improve clarity in defaultIfBlank method #1376. + Apache Commons Lang no longer builds on Android #1381. + Restrict size of cache to prevent overflow errors #1379. + Reimplement ClassUtils.hierarchy(Class, Interfaces) using an AtomicReference. + Fix Javadoc code examples in DiffBuilder and ReflectionDiffBuilder #1400. + Fix generics in Streams.toArray(Class) signature. + EventListenerSupport doesn't document ordering of events. + Fix edge-case NullPointerException in SystemUtils.IS_OS_ANDROID. + Fix edge-case NullPointerException in SystemUtils.isJavaVersionAtLeast(JavaVersion). + Fix edge-case NullPointerException in SystemUtils.isJavaVersionAtMost(JavaVersion). + Return the default enum if a SecurityException is caught in getEnumSystemProperty(). + Fix edge-case NullPointerException in EnumUtils.getEnum(Class, String, E). + EnumUtils.getFirstEnumIgnoreCase(Class, String, Function, E) now returns the given default enum on null enumClass input. + EnumUtils.getEnumIgnoreCase(Class, String, E) now returns the given default enum on null enumClass input. + EnumUtils.getEnumIgnoreCase(Class, String) now returns the given default enum on null enumClass input. + Fix NullPointerException in ComparableUtils.ComparableCheckBuilder.equalTo(A). + Fix NullPointerException in ComparableUtils.ComparableCheckBuilder.greaterThan(A). + Fix NullPointerException in ComparableUtils.ComparableCheckBuilder.greaterThanOrEqualTo(A). + Fix NullPointerException in ComparableUtils.ComparableCheckBuilder.lessThan(A). + Fix NullPointerException in ComparableUtils.ComparableCheckBuilder.lessThanOrEqualTo(A). + Use GitHub URL in POM for improved automation support. + + Add Strings and refactor StringUtils. + Add StopWatch.run([Failable]Runnable) and get([Failable]Supplier). + Add JavaVersion.JAVA_23. + Add JavaVersion.JAVA_24. + Add SystemUtils.IS_JAVA_23. + Add SystemUtils.IS_JAVA_24. + Add IntegerRange.toIntStream(). + Add LongRange.toLongStream(). + Add IntStreams.of(int...). + Add ArrayUtils.containsAny(int[], int...). + Add CalendarUtils.toLocalDate() #725. + Add SystemUtils.IS_OS_MAC_OSX_SEQUOIA. + Add BasicThreadFactory.builder() and deprecate BasicThreadFactory.Builder(). + Add BasicThreadFactory.daemon(). + Add ArrayUtils.startsWith. + Add Predicates. + Add RegExUtils methods typed to CharSequence input and deprecate old versions typed to String. + Add IterableStringTokenizer. + Add FailableIntToFloatFunction. + Add Validate.isTrue(boolean, Supplier<String>). + Add EnumUtils.getFirstEnum(Class<E>, int, ToIntFunction<E>, E). + Add FailableToBooleanFunction. + Add the @FunctionalInterface annotation to Computable. + Add SystemUtils.getJavaIoTmpDirPath(). + Add SystemUtils.getJavaHomePath(). + Add SystemUtils.getUserDirPath(). + Add SystemUtils.getUserHomePath(). + Add ArrayFill.fill(T[], FailableIntFunction)). + Add SystemProperties.JAVA_SECURITY_DEBUG. + Add SystemProperties.JAVA_SECURITY_KERBEROS_CONF. + Add SystemProperties.JAVA_SECURITY_KERBEROS_KDC. + Add SystemProperties.JAVA_SECURITY_KERBEROS_REAL. + Add ArrayFill.fill(boolean[], boolean) #1386. + Add ObjectUtils.getIfNull(Object, Object) and deprecate defaultIfNull(Object, Object). + Mutable now extends Supplier. + Add CharUtils.isHex(char). + Add CharUtils.isOctal(char). + Add LockingVisitors.reentrantLockVisitor(Object). + Add LockingVisitors.create(Object, ReentrantLock). + Add LockingVisitors.ReentrantLockVisitor. + Add builders for LockingVisitors implementations. + Add EnumSet.stream(Class). + Add SystemProperties.isPropertySet(String). + + Bump org.apache.commons:commons-parent from 73 to 85 #1267, #1277, #1283, #1288, #1302, #1377. + [site] Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #1300. + [test] Bump org.easymock:easymock from 5.4.0 to 5.6.0 #1317, #1387. + [test] Bump org.apache.commons:commons-text from 1.12.0 to 1.13.1 #1336. + + + + Using RandomStringUtils.insecure() still leads to using the secure() random. + Deprecate static RandomUtils.next*() methods in favor or .secure() and .insecure() versions. + Deprecate static RandomStringUtils.random*() methods in favor or .secure() and .insecure() versions. + RandomUtils.secure() now uses SecureRandom() instead of SecureRandom.getInstanceStrong(). + RandomStringUtils.secure() now uses SecureRandom() instead of SecureRandom.getInstanceStrong(). + Remove unused exception from deprecated StringUtils.toString(byte[], String). + + Make RandomUtils.insecure() public. + Add RandomUtils.secureStrong(). + Add RandomStringUtils.secureStrong(). + Add CalendarUtils.toLocalDateTime(Calendar). + Add CalendarUtils.toLocalDateTime(). + Add CalendarUtils.toZonedDateTime(Calendar). + Add CalendarUtils.toZonedDateTime(). + Add CalendarUtils.toOffsetDateTime(Calendar). + Add CalendarUtils.toOffsetDateTime(). + + Bump org.hamcrest:hamcrest from 2.2 to 3.0 #1255. + Bump org.easymock:easymock from 5.3.0 to 5.4.0 #1256. + Bump org.codehaus.mojo:exec-maven-plugin from 3.3.0 to 3.4.1 #1262, #1264. + Bump org.apache.commons:commons-parent from 72 to 73 #1265. + + + + Reimplement StopWatch internals to use java.time. + RandomStringUtils.random() with a negative character index should throw IllegalArgumentException. + LocaleUtils.toLocale(String) cannot parse four segments. + Use fewer intermediary strings in DefaultExceptionContext.getFormattedExceptionMessage(String). + Fix Javadoc in StringUtils.splitPreserveAllTokens() #1251. + Deprecate ArraySort constructor for removal. + Deprecate CharEncoding constructor for removal. + Deprecate Conversion constructor for removal. + Deprecate Conversion constructor for removal. + Deprecate EntityArrays constructor for removal. + Deprecate ObjectToStringComparator constructor for removal. + Deprecate RuntimeEnvironment constructor for removal. + + Add StopWatch.getSplitDuration() and deprecate getSplitTime(). + Add StopWatch.getStartInstant() and deprecate getStartTime(). + Add StopWatch.getStopInstant() and deprecate getStopTime(). + Add StopWatch.getDuration() and deprecate getTime(). + Add Javadoc links from StopWatch to DurationUtils #1249. + Add LangCollectors.collect(Collector, T...). + Add RandomStringUtils.secure(). + Add RandomStringUtils.insecure(). + + Bump org.apache.commons:commons-parent from 71 to 72 #1253. + + + + Customize text pattern in DiffResult#toString(). + Add DiffBuilder.Builder. + Add DiffBuilder.builder(). + Add ReflectionDiffBuilder.Builder. + Add ReflectionDiffBuilder.builder(). + Add test in TypeUtilsTest #1151. + Add Streams.failableStream(T), non-varargs variant. + Add Streams.nonNull(T), non-varargs variant. + Add ArrayUtils.nullTo(T[], T[]). + Add T ArrayUtils.arraycopy(T, int, T, int, int) fluent style. + Add T ArrayUtils.arraycopy(T, int, int, int, Function) fluent style. + Add SystemUtils.IS_JAVA_22. + Add JavaVersion.JAVA_22. + Add SystemProperties.getUserName(Supplier<String>). + Add SystemProperties.getLineSeparator(Supplier<String>). + Add SystemProperties.getJavaSpecificationVersion(Supplier<String>). + Add SystemProperties constants and methods for system properties as of Java 22. + Add MethodUtils.getMethodObject(Class, String, Class...). + Add null-safe Consumers.accept() and Functions.apply() #1215. + Add SystemUtils.IS_OS_ANDROID. + Add SystemUtils.IS_OS_MAC_OSX_SONOMA. + Add RuntimeEnvironment.inContainer() #1241. + Add AppendableJoiner and refactor string joining #1244. + + Improve Javadoc in ExceptionUtils #1136. + Fixed two non-deterministic tests in EnumUtilsTest.java #1131. + Fix wrong number check that cause StringIndexOutOfBoundsException #1140. + Rethrow NegativeArraySizeException as SerializationException in SerializationUtils.deserialize(InputStream) #1141. + Throw NumberFormatException instead of IndexOutOfBoundsException in NumberUtils.getMantissa(String, int) #1145. + Minor grammar fixes #1143. + ArrayUtils will return null when adding two null arrays, but undocumented. + Let parent POM figure out commons.spdx.version. + Undeprecate ExceptionUtils.rethrow(Throwable). + Test the Conversion class #1155. + Address minor redundancies after code inspection #1148. + Allow EventListenerSupport to handle (and ignore) exception from listeners allowing invocation of all listeners #1167. + Deprecate AnnotationUtils 0-argument constructor. + Deprecate ArchUtils 0-argument constructor. + Deprecate ArrayUtils 0-argument constructor. + Deprecate BooleanUtils 0-argument constructor. + Deprecate CharSequenceUtils 0-argument constructor. + Deprecate CharSetUtils 0-argument constructor. + Deprecate CharUtils 0-argument constructor. + Deprecate ClassLoaderUtils 0-argument constructor. + Deprecate ClassPathUtils 0-argument constructor. + Deprecate ClassUtils 0-argument constructor. + Deprecate ConstructorUtils 0-argument constructor. + Deprecate DateFormatUtils 0-argument constructor. + Deprecate DateUtils 0-argument constructor. + Deprecate Diff.getType(). + Deprecate DiffBuilder.DiffBuilder(T, T, ToStringStyle). + Deprecate DiffBuilder.DiffBuilder(T, T, ToStringStyle, boolean). + Deprecate DurationFormatUtils 0-argument constructor. + Deprecate DurationUtils 0-argument constructor. + Deprecate EnumUtils 0-argument constructor. + Deprecate EventUtils 0-argument constructor. + Deprecate FieldUtils 0-argument constructor. + Deprecate IEEE754rUtils 0-argument constructor. + Deprecate InheritanceUtils 0-argument constructor. + Deprecate IntStreams 0-argument constructor. + Deprecate LocaleUtils 0-argument constructor. + Deprecate LockingVisitors 0-argument constructor. + Deprecate MemberUtils 0-argument constructor. + Deprecate MethodUtils 0-argument constructor. + Deprecate NumberUtils 0-argument constructor. + Deprecate ObjectUtils 0-argument constructor. + Deprecate RandomStringUtils 0-argument constructor. + Deprecate RandomUtils 0-argument constructor. + Deprecate ReflectionDiffBuilder.ReflectionDiffBuilder(T, T, ToStringStyle). + Deprecate RegExUtils 0-argument constructor. + Deprecate SerializationUtils 0-argument constructor. + Deprecate Streams 0-argument constructor. + Deprecate StringEscapeUtils 0-argument constructor. + Deprecate StringUtils 0-argument constructor. + Deprecate Suppliers 0-argument constructor. + Deprecate SystemProperties 0-argument constructor. + Deprecate ThreadUtils 0-argument constructor. + Deprecate TypeUtils 0-argument constructor. + Make ArrayFill null-safe. + Make ArraySorter null-safe. + Make ArrayUtils.removeAll() null-safe. + Fix Java version in README.md #1170. + StringUtils.stripAccents() should handle ligatures, UTF32 math blocks, etc. #1201. + TypeUtils.toString(Type) StackOverflowError for an inner class in the inner class parameterized enclosing class #657. + Deprecate SystemUtils.getUserName(String) in favor of SystemProperties.getUserName(Supplier). + Make LockVisitor.acceptReadLocked(FailableConsumer) null-safe. + Make LockVisitor.applyWriteLocked(FailableConsumer) null-safe. + Make ObjectUtils.getFirstNonNull(Supplier...) null-safe. + Make SystemProperties.getLineSeparator(Supplier). + StringUtils.stripAccents(String) doesn't handle "\u0111" and "\u0110" (Vietnamese) #1216. + StringUtils.stripAccents(String) doesn't handle I with bar. + StringUtils.stripAccents(String) doesn't handle U with bar. + StringUtils.stripAccents(String) doesn't handle T with stroke. + Fix Javadoc for FluentBitSet.setInclusive(int, int) #1222. + Same Javadoc changes as [TEXT-234] #1223. + Remove duplicate static data in SerializationUtils.ClassLoaderAwareObjectInputStream. + Reimplement RandomUtils and RandomStringUtils on top of SecureRandom#getInstanceStrong() #1235. + DiffBuilder: Type constraint for method append(..., DiffResult) too strict #786. + + Bump commons-parent from 64 to 71 #1194, #1233. + Bump org.codehaus.mojo:exec-maven-plugin from 3.1.1 to 3.3.0 #1175, #1224. + Bump org.apache.commons:commons-text from 1.11.0 to 1.12.0 #1200. + Bump org.easymock:easymock from 5.2.0 to 5.3.0 #1232. + Bump org.codehaus.mojo:taglist-maven-plugin from 3.0.0 to 3.1.0 #1242. + + Drop obsolete JDK 13 Maven profile #1142. + + + + Rename variable names from 'clss' to 'clazz' #1087. + [Javadoc] ComparableUtils'c1' to 'comparable1', 'c2' to ' + [Javadoc] Remove 2.1 specific comment #1091. + ImmutablePair and ImmutableTriple implementation don't match final in Javadoc. + [Javadoc] Fix Incorrect Description in Processor isAarch64() #1093. + [Javadoc] Point to right getShortClassName flavor in Javadoc for relevant notes #1097. + Improve performance of StringUtils.isMixedCase() #1096. + ThreadUtils find methods should not return null items #1098. + ReflectionToStringBuilder changes in version 3.13.0 has broken the logic for overriding classes. + Return "null" instead of NPE in ClassLoaderUtils.toString(ClassLoader). + Return "null" instead of NPE in ClassLoaderUtils.toString(URLClassLoader). + Return ToStringStyle.nullText instead of NPE for ReflectionToStringBuilder.toString(). + Fix ThresholdCircuitBreaker#checkState() #1100. + Use ConcurrentInitializer implementations without subclassing. #1123. + Update critical value for chi-square test #1125. + Fix Javadoc syntax errors #1129. + + Add Functions#function(Function). + Add FailableFunction#function(FailableFunction). + Add CalendarUtils.getInstance(). + Add syntax for optional tokens to DurationFormatUtils #1062. + Add ArrayFill. + Add FastDateParser.TimeZoneStrategy.TzInfo.toString(). + Add LocaleUtils.isLanguageUndetermined(Locale). + Add ObjectUtils.toString(Supplier<Object>, Supplier<String>). + Add LazyInitializer.isInitialized(). + Add ConcurrentInitializer#isInitialized() #1120. + Add Streams.failableStream(T...). + Add FailableSupplier.nul(). + Add Suppliers.nul(). + Add ExceptionUtils.throwUnchecked(T) where T extends Throwable, and deprecate Object version. + Add ExceptionUtils.rethrowRuntimeException(T), and deprecate rethrow(T). + ConcurrentInitializer implementations can now be instantiated and configured with allocation and release lambdas. + Add support for RISC-V in ArchUtils #1128. + + Bump commons-parent from 58 to 64. + Bump org.easymock:easymock from 5.1.0 to 5.2.0 #1104. + Bump commons-text from 1.10.0 to 1.11.0. + Bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.1.1 #1135. + + + + NumberUtils.createNumber() to recognize hex integers prefixed with +. + NumberUtils.createNumber() to return requested floating point type for zero. + DMI: Random object created and used only once (DMI_RANDOM_USED_ONLY_ONCE); Better multi-threaded behavior. + Redundant Collection operation. Use Collections.emptyIterator() #738. + Make Streams.stream(Collection) null-safe. + Allow tests to access java.util classes such as ArrayList in Java 16 #788. + OpenJDK 16 Day Period Parsing #791. + Update documentation to list correct exception for null array parameters #785. + Fixing reversed Javadoc descriptions in StopWatch #781. + Fix typos in Javadoc #795. + Simplify assertions with equivalent but more simple. #792. + Avoid multiple equivalent occurrences of the same expression. #797. + Remove redundant initializers #800. + Fix ObjectUtils Javadocs #755. + Add test idea for RangeTest from PR #815 by Rushi98, but with a new comment. + Make Range constructors more generic #810. + Use final and Remove redundant String. #813, #816. + Use Set instead of List for checking the contains() method #734. + Javadoc for StringUtils.substringBefore(String str, int separator) doesn't mention that the separator is an int. + Fix NullPointerException in ThreadUtils.getSystemThreadGroup() when the current thread is stopped. + ArrayUtils.toPrimitive(Boolean...) null array elements map to false, like Boolean.parseBoolean(null) and its callers return false. + StrBuilder.StrBuilderReader.skip(long): Throw an exception when an implicit narrowing conversion in a compound assignment would result in information loss or a numeric error such as an overflows. + Deprecate Validate#notNull(Object) in favor of using Objects#requireNonNull(Object, String). + Use TimeZone from calendar in DateFormatUtils. + Updating javadoc for NullPointerException when Validate.notNull() is called #870. + Fixing and adding DateUtils exception Javadocs #871. + Improve performance of StringUtils.unwrap(String, String) #844. + Improve performance of StringUtils.join for primitives #812. + Fixed NPE getting Stack Trace if Throwable is null #733. + Make Validate.isAssignableFrom() check null inputs. + Fix Javadoc for Validate.isAssignableFrom(). + Make final mappingFunction variable #876. + Remove unnecessary variable creations #882. + Minor changes #769. + FastDateFormat does not support the 'L'-Pattern from SimpleDateFormat. + Increase test coverage of ComparableUtils from 71% to 100% #898. + Increase method test coverage of MultilineRecursiveToStringStyle #899. + Fix unstable coverage of CharSequenceUtils tests noticed during merge of PRs 898 and 899 #901. + Rewrite Conversion.binaryBeMsb0ToHexDigit to invert logic of binaryToHexDigit. + Allow extension of previously final classes ImmutablePair and ImmutableTriple. + Update ClassUtils Javadoc with some missing throws NPE #912. + Javadoc: StringUtils.repeat("", "x", 3) = "xx"; #918. + Fix typos #920, #923. + Simplify condition #925. + StringUtils.join(Iterable, String) should only return null when the Iterable is null. + StringUtils.join(Iterator, String) should only return null when the Iterator is null. + Add tests to increase coverage #904. + Extends Object clauses are redundant #937. + Simplify conditional expression. #941. + Fix some Javadoc comments #938. + Deprecate getNanosOfMiili() method with typo and create proper getNanosOfMilli() #940. + Deprecate ThreadUtils code that defines custom function interfaces in favor of stock java.util.function.Predicate usage. + Fix links in Javadoc and documentation #926. + Deprecate RandomUtils in favor of Apache Commons RNG UniformRandomProvider #942. + Added docs regarding week year support #924. + ClassUtils.getShortCanonicalName doesn't use the canonicalName #949. + Validate: Get error messages without using String.format when varargs is empty. + Simplify expression (length is never < 0) #962. + Fix simple broken javadoc. #981. + Fix typo #1001. + Use Objects.requireNonNull() directly #1022. + MethodUtils.getMatchingMethod() fails with "Found multiple candidates" #1033. + Construct ArrayList with better default size #1041. + ThreadUtilsTest#testThreadGroups will test failed when using Junit5 parallel test #1051. + Swap the order of assertion args (first excepted then actual) #1054. + Fix the comment of Failable, redundant "-" #1056. + Fix the comment of ComparableUtils, using "smallest", not "largest" #1058. + AnnotationUtilsTest and FormattableUtilsTest Only use static imports to import assert methods in tests #1052. + [LANG-1681] Fix some FieldUtils Javadocs #1047. + Remove unnecessary statement in DurationFormatUtils #965. + Corrected value of SystemUtils.JAVA_VENDOR #1066. + [StepSecurity] ci: Harden GitHub Actions #1067. + Update Javadoc for the insert methods in ArrayUtils #1078. + Deprecate ExceptionUtils.ExceptionUtils(). + TypeUtils.getRawType() throws a NullPointerException on Wildcard GenericArrayType. + Throw IllegalArgumentException instead of InternalError in the builder package. + Avoid NPE in MutableObject#equals() for null content. + SystemUtils fix and updates related to macOS #1085. + ThreadLocalRandom should be used in utility classes. + MethodUtils.getAnnotation fails with "Found multiple candidates for method ...". + + Add GitHub coverage.yml. + Add EnumUtils.getEnumSystemProperty(...). + Add TriConsumer. + Add and use EnumUtils.getFirstEnumIgnoreCase(Class, String, Function, E). + Add and use Suppliers. + Add and use ArrayUtils.getComponentType(T[]). + Add and use ClassUtils.getComponentType(Class>T[]>). + Add and use ObjectUtils.getClass(T). + Add and use ArrayUtils.newInstance(Class>T>, int). + Add and use null-safe Streams.of(T...). + Add ClassUtils.comparator(). + Add and use ThreadUtils.sleepQuietly(Duration). + Add and use ArrayUtils.setAll(T[], IntFunction). + Add and use ArrayUtils.setAll(T[], Supplier). + Add BooleanConsumer. + Add IntToCharFunction. + Add IntStreams. + Add UncheckedFuture. + Add UncheckedException. + Add UncheckedExecutionException. + Add UncheckedTimeoutException. + Add UncheckedInterruptedException. + Add TimeZones.GMT. + Add ObjectUtils.identityHashCodeHex(Object). + Add ObjectUtils.hashCodeHex(Object). + Add StringUtils.removeStart(String, char). + Add null-safe ObjectUtils.isArray() #754. + Add ComparableUtils.max(A, A) and ComparableUtils.min(A, A). + Add UncheckedReflectiveOperationException. + Add and use ClassUtils.isPublic(Class). + Add UncheckedIllegalAccessException. + Add MethodInvokers. + Add Streams.nullSafeStream(Collection). + Add Streams.toStream(Collection). + Add Streams.failableStream(Collection) and deprecate misnamed stream(Collection). + Add Streams.failableStream(Stream) and deprecate misnamed stream(Stream). + Add EnumUtils.getEnumMap(Class, Function). #730 + Add FluentBitSet. + Add Streams.instancesOf(Class, Collection). + Add ImmutablePair.ofNonNull(L, R). + Add ImmutableTriple.ofNonNull(L, M, R). + Add MutablePair.ofNonNull(L, R). + Add MutableTriple.ofNonNull(L, M, R). + Add Pair.ofNonNull(L, R). + Add Triple.ofNonNull(L, M, R). + Add ArrayUtils.containsAny(Object[], Object...). + Add Processor.Type.AARCH_64. + Add Processor.isAarch64(). + Update ArchUtils.getProcessor(String) for "aarch64". + Add JavaVersion.JAVA_18. + Add JavaVersion.JAVA_19. + Add JavaVersion.JAVA_20. + Add JavaVersion.JAVA_21. + Add TimeZones.toTimeZone(TimeZone). + Add FutureTasks. + Add Memoizer(Function) and Memoizer(Function, boolean). + Add Consumers. + Add github/codeql-action. + Add coverage.yml. + Add DurationUtils.since(Temporal). + Add DurationUtils.of(FailableConsumer|FailableRunnbale). + Add ExceptionUtils.forEach(Throwable, Consumer<Throwable>). + Add ExceptionUtils.stream(Throwable). + Add ExceptionUtils.getRootCauseStackTraceList(Throwable). + Add SystemUtils.IS_OS_WINDOWS_11. + Add SystemUtils.IS_JAVA_16. + Add SystemUtils.IS_JAVA_17. + Add SystemUtils.IS_JAVA_18. + Add SystemUtils.IS_JAVA_19. + Add SystemUtils.IS_JAVA_20. + Add SystemUtils.IS_JAVA_21. + Add ArrayUtils.oneHot(). + Let ReflectionToStringBuilder only reflect given field names #849. + Add Streams.of(Enumeration<E>). + Add Streams.of(Iterable<E>). + Add Streams.of(Iterator<E>). + Simple support for Optional in ObjectUtils#isEmpty() #933. + Add Processor.Type.getLabel(). + Add Processor.toString(). + Add HashCodeBuilder.equals(Object). + Add BooleanUtils.values() and forEach(). + Add ClassPathUtils.packageToPath(String) and pathToPackage(String) + Add CalendarUtils#getDayOfYear() #968 + Add NumberRange, DoubleRange, IntegerRange, LongRange. + Add missing exception javadoc/tests for some null arguments #869. + Add ClassLoaderUtils.getSystemURLs() and getThreadURLs(). + Add RegExUtils.dotAll() and dotAllMatcher(). + Add Pair.accept(FailableBiConsumer). + Add Pair.apply(FailableBiFunction). + Add ReflectionDiffBuilder.setExcludeFieldNames(...) and DiffExclude aâ€Ļ #838. + Add and ExceptionUtils.isChecked() and isUnchecked() #1069 + Add and use ExceptionUtils.throwUnchecked(throwable). + Add LockingVisitors.create(O, ReadWriteLock). + + Bump actions/cache from 2.1.4 to 3.0.10 #742, #752, #764, #833, #867, #959, #964. + Bump actions/checkout from 2 to 3.1.0 #819, #825, #859, #963. + Bump actions/setup-java from v1.4.3 to 3.5.1 #879. + Bump spotbugs-maven-plugin from 4.2.0 to 4.7.3.0 #735, #808, #822, #834, #868, #895, #919, #927, #946, #989. + Bump spotbugs from 4.2.2 to 4.7.3 #744, #917, #947, #973. + Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #943. + Bump checkstyle from 8.41 to 9.3 #739, #768, #787, #811, #824, #843. + Bump easymock from 4.2 to 5.1.0 #746, #972, #986, #1012. + Bump commons.jacoco.version from 0.8.6 to 0.8.8. + Bump commons.japicmp.version from 0.15.2 to 0.16.0. + Bump junit-pioneer from 1.3.8 to 1.9.1 #749, #767, #832, #883, #988, #991, #995. + Bump junit-bom from 5.7.1 to 5.9.1 #761, #805, #807, #836, #928, #955. + Bump maven-javadoc-plugin from 3.2.0 to 3.4.1. + Bump jmh.version from 1.27 to 1.36 #794, #842, #872, #990. + Bump maven-pmd-plugin from 3.14.0 to 3.19.0 #802, #858, #909, #948. + Bump pmd from 6.40.0 to 6.52.0 #837, #861, #873, #905, #915, #932, #944. + Bump biz.aQute.bndlib from 5.3.0 to 6.3.1 #814, #835. + Bump maven-bundle-plugin from 5.1.1 to 5.1.2. + Bump animal-sniffer-maven-plugin from 1.19 to 1.21. + Bump exec-maven-plugin from 1.6.0 to 3.1.0 #590, #922. + Bump maven-surefire-plugin from 3.0.0-M5 to 3.0.0-M7 #880, #910. + Bump apache-rat from 0.13 to 0.14. + Bump commons-parent from 53 to 58 #954, #1000, #1011, #1061. + Bump commons-text from 1.9 to 1.10.0 #957. + Bump commons.pmd-impl.version from 6.49.0 to 6.51.0 #961. + + + + + Correct implementation of RandomUtils.nextLong(long, long) + Restore handling of collections for non-JSON ToStringStyle #610. + ContextedException Javadoc add missing semicolon #581. + Resolve JUnit pioneer transitive dependencies using JUnit BOM. + NumberUtilsTest - incorrect types in min/max tests #634. + Improve StringUtils.stripAccents conversion of remaining accents. + StringUtils.countMatches - clarify Javadoc. + Remove redundant argument from substring call. + BigDecimal is created when you pass it the min and max values, #642. + ArrayUtils.contains() and indexOf() fail to handle Double.NaN #647. + ArrayUtils contains() and indexOf() fail to handle Float.NaN # #561. + Fix potential NPE in TypeUtils.isAssignable(Type, ParameterizedType, Map, Type>). + TypeUtils.isAssignable returns wrong result for GenericArrayType and ParameterizedType, #643. + testGetAllFields and testGetFieldsWithAnnotation sometimes fail. + Fix Javadoc for SystemUtils.isJavaVersionAtMost() #638. + Fix StringUtils.unwrap throws StringIndexOutOfBoundsException #636. + Fix formatting of isAnyBlank() and isAnyEmpty(). #513. + TypeUtils. containsTypeVariables does not support GenericArrayType #661. + Javadoc of some methods incorrectly refers to another method, #667, #668. #670. + Refine StringUtils.lastIndexOfIgnoreCase #664. + Refine StringUtils.abbreviate #663. + Refine StringUtils.isNumericSpace #573. + Refine StringUtils.deleteWhitespace #569. + Correction in Javadoc of some methods. #673 + Javadoc for RandomStringUtils.random() letters, numbers parameters is wrong. + Correct markup in Javadoc for unbalanced braces #679. + MethodUtils.invokeMethod NullPointerException in case of null in args list #680. + Fix 2 digit week year formatting #688. + Fix broken Javadoc links to commons-text #712. + Add and use ThreadUtils.sleep(Duration). + Add and use ThreadUtils.join(Thread, Duration). + Add ObjectUtils.wait(Duration). + + Add BooleanUtils.booleanValues(). + Add BooleanUtils.primitiveValues(). + Add StringUtils.containsAnyIgnoreCase(CharSequence, CharSequence...). + Add StopWatch.getStopTime(). + More test coverage for CharSequenceUtils. #631. + ArrayUtils.toPrimitive(Object) does not support boolean and other types #607. + Add fluent-style ArraySorter. + Add and use LocaleUtils.toLocale(Locale) to avoid NPEs. + Add FailableShortSupplier, handy for JDBC APIs. + Add JavaVersion.JAVA_17. + Add missing boolean[] join method #686. + Add StringUtils.substringBefore(String, int). + Add Range.INTEGER. + Add DurationUtils. + Introduce the use of @Nonnull, and @Nullable, and the Objects class as a helper tool. + Add and use true and false String constants #714. + Add and use ObjectUtils.requireNonEmpty() #716. + + Enable Dependabot #587. + Bump junit-jupiter from 5.6.2 to 5.7.0. + Bump spotbugs from 4.1.2 to 4.2.2, #627, #671, #708, #726. + Bump spotbugs-maven-plugin from 4.0.0 to 4.2.0, #593, #596, #609, #623, #632, #692. + Bump biz.aQute.bndlib from 5.1.1 to 5.3.0 #592, #628, #715. + Bump junit-pioneer from 0.6.0 to 1.1.0, #589, #597, #600, #624, #625, #662. + Bump checkstyle from 8.34 to 8.41, #594, #614, #637, #665, #706, #722. + Bump actions/checkout from v2.3.1 to v2.3.4 #601, #639. + Bump actions/setup-java from v1.4.0 to v1.4.2 #612. + Update commons.jacoco.version 0.8.5 to 0.8.6 (Fixes Java 15 builds). + Update maven-surefire-plugin 2.22.2 -> 3.0.0-M5. + Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #660. + Bump jmh.version from 1.21 to 1.27 #674. + Update commons.japicmp.version 0.14.3 -> 0.15.2. + Processor.java: check enum equality with == instead of .equals() method #690. + Bump junit-pioneer from 1.1.0 to 1.3.8, #702, #721. + Bump maven-checkstyle-plugin from 3.1.1 to 3.1.2 #705. + Bump actions/cache from v2 to v2.1.4 #710. + Bump junit-bom from 5.7.0 to 5.7.1 #707. + Minor Improvements #701. + Minor Improvement: Add final variable.try to make the code read-only #700. + Minor Improvement: Remove redundant initializer #699. + Use own validator ObjectUtils.anyNull to check null String input #718. + Bump commons-parent from 52 to 53 #885. + + + Refine test output for FastDateParserTest + CharSequenceUtils.lastIndexOf : remake it + remove encoding and docEncoding and use inherited values from commons-parent + Fix Javadoc for StringUtils.appendIfMissingIgnoreCase() #507. + Simplify null checks in Pair.hashCode() using Objects.hashCode(). #517. + Simplify null checks in Triple.hashCode() using Objects.hashCode(). #516. + Simplify some if statements in StringUtils. #521. + Simplify a null check in the private replaceEach() method of StringUtils. #514. + Replace some usages of the ternary operator with calls to Math.max() and Math.min() #512. + (Javadoc) Fix return tag for throwableOf*() methods #518. + Add ArrayUtils.isSameLength() to compare more array types #430. + CharSequenceUtils.regionMatches is wrong dealing with Georgian. + Added the Locks class as a convenient possibility to deal with locked objects. + Add to Functions: FailableBooleanSupplier, FailableIntSupplier, FailableLongSupplier, FailableDoubleSupplier, and so on. + Add ArrayUtils.get(T[], index, T) to provide an out-of-bounds default value. + Optimize ArrayUtils::isArrayIndexValid method. #551. + Use List.sort instead of Collection.sort #546. + Use StandardCharsets.UTF_8 #548. + Use Collections.singletonList insteadof Arrays.asList when there be only one element. #549. + Refine Javadoc #545. + Change array style from `int a[]` to `int[] a` #537. + Change from addAll to constructors for some List #536. + Fix typos #539. + Ignored exception `ignored`, should not be called so #540. + Simplify if as some conditions are covered by others #543. + StringUtils.replaceEachRepeatedly gives IllegalStateException #505. + Add JavaVersion enum constants for Java 14 and 15. #553. + Add JavaVersion enum constants for Java 16. + Use Java 8 lambdas and Map operations. + Change removeLastFieldSeparator to use endsWith #550. + Change a Pattern to a static final field, for not letting it compile each time the function invoked. #542. + Add ImmutablePair factory methods left() and right(). + Add ObjectUtils.toString(Object, Supplier<String>). + Fixed Javadocs for setTestRecursive() #556. + ToStringBuilder.reflectionToString - Wrong JSON format when object has a List of Enum. + [JSON string for maps] ToStringBuilder.reflectionToString doesn't render nested maps correctly. + Make CharSequenceUtils.toCharArray(CharSequence) public. + Add StringUtils.substringAfter(String, int). + Add StringUtils.substringAfterLast(String, int). + Correct Javadocs of methods that use Validate.notNull() and replace some uses of Validate.isTrue() with Validate.notNull(). #525. + Add allNull() and anyNull() methods to ObjectUtils. #522. + org.apache.commons:commons-parent 50 -> 51. + org.junit-pioneer:junit-pioneer 0.5.4 -> 0.6.0. + org.junit.jupiter:junit-jupiter 5.6.0 -> 5.6.2. + com.github.spotbugs:spotbugs 4.0.0 -> 4.0.6. + com.puppycrawl.tools:checkstyle 8.29 -> 8.34. + commons.surefire.version 3.0.0-M4 -> 3.0.0-M5. + - + + Make test more stable by wrapping assertions in hashset. Generate Javadoc jar on build. Add ExceptionUtils.throwableOfType(Throwable, Class) and friends. Add EMPTY_ARRAY constants to classes in org.apache.commons.lang3.tuple. Add null-safe StringUtils APIs to wrap String#getBytes([Charset|String]). - Add zero arg constructor for org.apache.commons.lang3.NotImplementedException. + Add zero arg constructor for NotImplementedException. Add ArrayUtils.addFirst() methods. Remove redundant if statements in join methods #411. Trivial: year of release for 3.9 says 2018, should be 2019 @@ -70,7 +1094,7 @@ The type attribute can be add,update,fix,remove. Corrected usage examples in Javadocs #458. Improve Javadoc based on the discussion of the GitHub PR #459. maven-checkstyle-plugin 3.0.0 -> 3.1.0. - Update documentation related to the issue LANG-696 #449. + Update documentation and tests related to the issue LANG-696 #449. AnnotationUtils little cleanup #467. Add org.apache.commons.lang3.time.Calendars. Add EnumUtils getEnum() methods with default values #475. @@ -90,15 +1114,29 @@ The type attribute can be add,update,fix,remove. Add StopWatch convenience APIs to format times and create a simple instance. Allow a StopWatch to carry an optional message. Add ComparableUtils #398. - Add org.apache.commons.lang3.SystemUtils.getUserName(). + Add SystemUtils.getUserName(). Add ObjectToStringComparator. #483. - Add org.apache.commons.lang3.arch.Processor.Arch.getLabel(). - Add IS_JAVA_14 and IS_JAVA_15 to org.apache.commons.lang3.SystemUtils. + Add Processor.Arch.getLabel(). + Add SystemUtils.IS_JAVA_14. + Add SystemUtils.IS_JAVA_15. ObjectUtils: Get first non-null supplier value. Added the Streams class, and Functions.stream() as an accessor thereof. + org.easymock:easymock 4.1 -> 4.2. + org.junit-pioneer:junit-pioneer 0.4.2 -> 0.5.4. + org.junit.jupiter:junit-jupiter 5.5.2 -> 5.6.0. + Use Javadoc {@code} instead of pre tags. #490. + ExceptionUtilsTest to 100% #486. + MethodUtils will throw a NPE if invokeMethod() is called for a var-args method #407. + Reuse own code in Functions.java #493. + MethodUtils.getAnnotation() with searchSupers = true does not work if super is generic #494. + Avoid unnecessary allocation in StringUtils.wrapIfMissing. #496. + Internally use Validate.notNull(foo, ...) instead of Validate.isTrue(foo != null, ...). + Add 1 and 0 in toBooleanObject(final String str) #502. + Remove a redundant argument check in NumberUtils #504. + Deprecate ArrayUtils.removeAllOccurences(*) for ArrayUtils.removeAllOccurrences(*). - + FieldUtils.removeFinalModifier(Field, boolean), in java 12 throw exception because the final modifier is no longer mutable. Switch coverage from cobertura to jacoco. @@ -110,9 +1148,9 @@ The type attribute can be add,update,fix,remove. Update Java Language requirement to 1.8 Add isEmpty method to ObjectUtils Add null-safe StringUtils.valueOf(char[]) to delegate to String.valueOf(char[]) - Add API org.apache.commons.lang3.SystemUtils.isJavaVersionAtMost(JavaVersion) + Add API SystemUtils.isJavaVersionAtMost(JavaVersion) Consolidate the StringUtils equals and equalsIgnoreCase Javadoc and implementation - (doc) Fix javadoc for 'startIndex' parameter of StringUtils.join() methods. GitHub PR #412. + (doc) Fix javadoc for 'startIndex' parameter of StringUtils.join() methods. GitHub PR #412. @@ -132,17 +1170,17 @@ The type attribute can be add,update,fix,remove. Fix EventCountCircuitBreaker increment batch NumberUtils.createNumber() throws StringIndexOutOfBoundsException instead of NumberFormatException WordUtils.wrap throws StringIndexOutOfBoundsException when wrapLength is Integer.MAX_VALUE. - Typo in JavaDoc for lastIndexOf + Typo in Javadoc for lastIndexOf ObjectUtils.identityToString(Object) and friends should allocate builders and buffers with a size EnumUtils.getEnumIgnoreCase and isValidEnumIgnoreCase methods added Add ToStringSummary annotation Add bypass option for classes to recursive and reflective EqualsBuilder - Improve Javadoc for StringUtils.isAnyEmpty(null) - Add API SystemUtils.String getEnvironmentVariable(final String name, final String defaultValue) - org.apache.commons.lang3.SystemUtils should not write to System.err. + Improve Javadoc for StringUtils.isAnyEmpty(null) + Add API SystemUtils.String getEnvironmentVariable(final String name, final String defaultValue) + SystemUtils should not write to System.err. Add RegexUtils class instead of overloading methods in StringUtils that take a regex to take precompiled Pattern. StringUtils.join() with support for List<?> with configurable start/end indices. - Methods for getting first non empty or non blank value + Methods for getting first non-empty or non-blank value Remove checks for java versions below the minimum supported one Null/index safe get methods for ArrayUtils Rounding utilities for converting to BigDecimal @@ -154,7 +1192,7 @@ The type attribute can be add,update,fix,remove. StackOverflowError on TypeUtils.toString(...) for a generic return type of Enum.valueOf ConstructorUtils.invokeConstructor(Class, Object...) regression EqualsBuilder#isRegistered: swappedPair construction bug - org.apache.commons.lang3.time.FastDateParser should use toUpperCase(Locale) + FastDateParser should use toUpperCase(Locale) Improve StringUtils#replace throughput Remove deprecation from RandomStringUtils ExceptionUtils.getThrowableList() is using deprecated ExceptionUtils.getCause() @@ -175,16 +1213,16 @@ The type attribute can be add,update,fix,remove. Add JMH maven dependencies Add null filter to ReflectionToStringBuilder LocaleUtils#toLocale does not support language followed by UN M.49 numeric-3 area code followed by variant - Clarify or improve behaviour of int-based indexOf methods in StringUtils + Clarify or improve behavior of int-based indexOf methods in StringUtils Add method for converting string to an array of code points - RandomStringUtils random method can overflow and return characters outside of specified range + RandomStringUtils random method can overflow and return characters outside the specified range Add methods to insert arrays into arrays at an index WordUtils.wrap throws StringIndexOutOfBoundsException RandomStringUtils#random can enter infinite loop if end parameter is to small NullPointerException in FastDateParser$TimeZoneStrategy Javadoc of StringUtils.ordinalIndexOf is contradictory. StringUtils#join(T...): warning: [unchecked] Possible heap pollution from parameterized vararg type T - Multiple calls of org.apache.commons.lang3.concurrent.LazyInitializer.initialize() are possible. + Multiple calls of LazyInitializer.initialize() are possible. StrBuilder#replaceAll ArrayIndexOutOfBoundsException BooleanUtils javadoc issues ArrayUtils#add confusing example in javadoc @@ -192,11 +1230,11 @@ The type attribute can be add,update,fix,remove. Add StringUtils#unwrap Add support for recursive comparison to EqualsBuilder#reflectionEquals Add a reflection-based variant of DiffBuilder - Implementation of a Memomizer + Implementation of a Memoizer Add ArrayUtils#toStringArray method StringUtils#abbreviate should support 'custom ellipses' parameter Add StringUtils#isAllEmpty and #isAllBlank methods - Increase test coverage of org.apache.commons.lang3.ArrayUtils + Increase test coverage of ArrayUtils StrSubstitutor should state its thread safety StringUtils#getLevenshteinDistance reduce memory consumption Update Java requirement from Java 6 to 7. @@ -236,7 +1274,7 @@ The type attribute can be add,update,fix,remove. StringUtils#normalizeSpace does not trim the string anymore SerializationUtils.ClassLoaderAwareObjectInputStream should use static initializer to initialize primitiveTypes map [GitHub issue #170] Add RandomUtils#nextBoolean() method - FastDatePrinter Memory allocation regression + FastDatePrinter memory allocation regression FastDatePrinter generates extra Date objects Fix precision loss on NumberUtils.createNumber(String) HashCodeBuilder.append(Object,Object) is too big to be inlined, which prevents whole builder to be scalarized @@ -245,13 +1283,13 @@ The type attribute can be add,update,fix,remove. Enhance MethodUtils to allow invocation of private methods Fix implementation of StringUtils.getJaroWinklerDistance() Fix dead links in StringUtils.getLevenshteinDistance() javadoc - "\u2284":"⊄" mapping missing from EntityArrays#HTML40_EXTENDED_ESCAPE + "\u2284":"nsub" mapping missing from EntityArrays#HTML40_EXTENDED_ESCAPE Simplify ArrayUtils removeElements by using new decrementAndGet() method Add getAndIncrement/getAndDecrement/getAndAdd/incrementAndGet/decrementAndGet/addAndGet in Mutable* classes Optimize BitField constructor implementation Improve CharSetUtils.squeeze() performance Add RandomStringUtils#randomGraph and #randomPrint which match corresponding regular expression class - StringUtils#startsWithAny/endsWithAny is case sensitive - documented as case insensitive + StringUtils#startsWithAny/endsWithAny is case-sensitive - documented as case insensitive Add StopWatch#getTime(TimeUnit) Add methods to ObjectUtils class to check for null elements in the array Prefer Throwable.getCause() in ExceptionUtils.getCause() @@ -266,11 +1304,11 @@ The type attribute can be add,update,fix,remove. EqualsBuilder.append(Object,Object) is too big to be inlined, which prevents whole builder to be scalarized NumberUtils.createNumber() behaves inconsistently with NumberUtils.isNumber() Add support for varargs in ConstructorUtils, MemberUtils, and MethodUtils - Add methods to check numbers against NaN and inifinite to Validate + Add methods to check numbers against NaN and infinite to Validate Fix for incorrect comment on StringUtils.containsIgnoreCase method Fix typo on appendIfMissing javadoc Add tests for missed branches in DateUtils - parseDateStrictly does't pass specified locale + parseDateStrictly doesn't pass specified locale FastDateFormat doesn't respect summer daylight in some localized strings z/OS identification in SystemUtils StringUtils#startsWithAny has error in Javadoc @@ -340,7 +1378,7 @@ The type attribute can be add,update,fix,remove. Make logic for comparing OS versions in SystemUtils smarter Shutdown thread pools in test cases FastDateParser and FastDatePrinter support 'X' format - Avoid memory allocation when using date formating to StringBuffer + Avoid memory allocation when using date formatting to StringBuffer Possible performance improvement on string escape functions Exception while using ExtendedMessageFormat and escaping braces Avoid String allocation in StrBuilder.append(CharSequence) @@ -348,7 +1386,7 @@ The type attribute can be add,update,fix,remove. Update org.easymock:easymock to 3.3.1 Update maven-pmd-plugin to 3.4 Update maven-antrun-plugin to 1.8 - Wrong formating of time zones with daylight saving time in FastDatePrinter + Wrong formatting of time zones with daylight saving time in FastDatePrinter Performance improvements for StringEscapeUtils Add ClassUtils.getAbbreviatedName() FastDateParser does not set error indication in ParsePosition @@ -375,7 +1413,7 @@ The type attribute can be add,update,fix,remove. Javadoc for EqualsBuilder.reflectionEquals() is unclear Improve performance of normalize space Add StringUtils.countMatches(CharSequence, char) - org.apache.commons.lang3.SystemUtils#isJavaVersionAtLeast should return true by default + SystemUtils#isJavaVersionAtLeast should return true by default Provide methods to retrieve all fields/methods annotated with a specific type Bring static method references in StringUtils to consistent style NumberUtils#isParsable method(s) @@ -394,13 +1432,13 @@ The type attribute can be add,update,fix,remove. Javadoc is not clear on preferred pattern to instantiate FastDateParser / FastDatePrinter FastDateParser should be case insensitive Fix bug with stripping spaces on last line in WordUtils.wrap() - Add method org.apache.commons.lang3.reflect.MethodUtils.invokeExactMethod(Object, String) - Add method org.apache.commons.lang3.reflect.MethodUtils.invokeMethod(Object, String) + Add MethodUtils.invokeExactMethod(Object, String) + Add MethodUtils.invokeMethod(Object, String) NumberUtils#isNumber() returns false for "0.0", "0.4790", et al - Add org.apache.commons.lang3.SystemUtils.IS_JAVA_1_8 + Add SystemUtils.IS_JAVA_1_8 @@ -412,7 +1450,7 @@ The type attribute can be add,update,fix,remove. Failing tests with Java 8 b128 - + ReflectionToStringBuilder.toString does not debug 3rd party object fields within 3rd party object Add methods for removing all invalid characters according to XML 1.0 and XML 1.1 in an input string to StringEscapeUtils NumericEntityEscaper incorrectly encodes supplementary characters @@ -423,7 +1461,7 @@ The type attribute can be add,update,fix,remove. Add APIs MutableBoolean setTrue() and setFalse() ConstantInitializerTest fails when building with IBM JDK 7 Add SerializationUtils.roundtrip(T extends Serializable) to serialize then deserialize - org.apache.commons.lang3.reflect.FieldUtils.removeFinalModifier(Field) does not clean up after itself + FieldUtils.removeFinalModifier(Field) does not clean up after itself FastDateParser javadoc incorrectly states that SimpleDateFormat is used internally There should be a DifferenceBuilder with a ReflectionDifferenceBuilder implementation uncaught PatternSyntaxException in FastDateFormat on Android @@ -509,9 +1547,9 @@ The type attribute can be add,update,fix,remove. FastDateParser does not handle non-Gregorian calendars properly FastDateParser does not handle non-ASCII digits correctly Create StrBuilder APIs similar to String.format(String, Object...) - NumberUtils#createNumber - bad behaviour for leading "--" + NumberUtils#createNumber - bad behavior for leading "--" FastDateFormat's "z" pattern does not respect timezone of Calendar instances passed to format() - Add org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS_8 + Add SystemUtils.IS_OS_WINDOWS_8 StringUtils.equalsIgnoreCase doesn't check string reference equality StringUtils.join() endIndex, bugged for loop RandomStringUtils throws confusing IAE when end <= start @@ -538,7 +1576,7 @@ The type attribute can be add,update,fix,remove. - Add API StringUtils.toString(byte[] intput, String charsetName) + Add API StringUtils.toString(byte[] input, String charsetName) Add an example with whitespace in StringUtils.defaultIfEmpty Add APIs ClassUtils.isPrimitiveWrapper(Class<?>) and isPrimitiveOrWrapper(Class<?>) Fix createLong() so it behaves like createInteger() @@ -586,24 +1624,24 @@ The type attribute can be add,update,fix,remove. Add ClassUtils.getSimpleName() methods. Add hashCodeMulti varargs method. Removed DateUtils.UTC_TIME_ZONE. - Convert more of the StringUtils API to take CharSequence. + Convert more of the StringUtils API to take CharSequence. EqualsBuilder synchronizes on HashCodeBuilder. StringUtils.isAlpha, isAlphanumeric and isNumeric now return false for "". Add support for ConcurrentMap.putIfAbsent(). Documented potential NPE if auto-boxing occurs for some BooleanUtils methods. - DateUtils.isSameLocalTime compares using 12 hour clock and not 24 hour. + DateUtils.isSameLocalTime compares using 12-hour clock and not 24-hour. Extend exception handling in ConcurrentUtils to runtime exceptions. SystemUtils.getJavaVersionAsFloat throws StringIndexOutOfBoundsException on Android runtime/Dalvik VM. WordUtils.abbreviate() removed. Doc bug in DateUtils#ceiling. StringEscapeUtils.unescapeJava doesn't handle octal escapes and Unicode with extra u. - org.apache.commons.lang3.math.Fraction does not reduce (Integer.MIN_VALUE, 2^k). - org.apache.commons.lang3.math.Fraction does not always succeed in multiplyBy and divideBy. + Fraction does not reduce (Integer.MIN_VALUE, 2^k). + Fraction does not always succeed in multiplyBy and divideBy. Change ObjectUtils min() & max() functions to use varargs rather than just two parameters. Add a Null-safe compare() method to ObjectUtils. NumberUtils.isNumber(String) is not right when the String is "1.1L". EntityArrays typo: {"\u2122", "&minus;"}, // minus sign, U+2212 ISOtech. - Some Entitys like &Ouml; are not matched properly against its ISO8859-1 representation. + Some entities like &Ouml; are not matched properly against its ISO8859-1 representation. Example StringUtils.indexOfAnyBut("zzabyycdxx", '') = 0 incorrect. Add StringUtils.defaultIfBlank(). Provide a very basic ConcurrentInitializer implementation. @@ -621,8 +1659,8 @@ The type attribute can be add,update,fix,remove. Add normalizeSpace to StringUtils. NumberUtils createNumber throws a StringIndexOutOfBoundsException when argument containing "e" and "E" is passed in. - NOTE: The below were included in the Commons Lang 3.0-beta release. - Convert StringUtils API to take CharSequence. + NOTE: The below were included in the Commons Lang 3.0-beta release. + Convert StringUtils API to take CharSequence. Push down WordUtils to "text" sub-package. Extend exception handling in ConcurrentUtils to runtime exceptions. Some StringUtils methods should take an int character instead of char to use String API features. @@ -725,7 +1763,7 @@ The type attribute can be add,update,fix,remove. MemberUtils: getMatchingAccessibleMethod does not correctly handle inheritance and method overloading. Javadoc is incorrect for lastIndexOf() method. Javadoc for HashCodeBuilder.append(boolean) does not match implementation. - Javadoc StringUtils.left() claims to throw an exception on negative lenth, but doesn't. + Javadoc StringUtils.left() claims to throw an exception on negative length, but doesn't. Javadoc - document thread safety. Test for StringUtils replaceChars() icelandic characters. @@ -769,7 +1807,7 @@ The type attribute can be add,update,fix,remove. FastDateFormat - call getTime() on a calendar to ensure timezone is in the right state. FastDateFormat - Remove unused field. LocaleUtils - Initialization of available locales in LocaleUtils can be deferred. - NumberUtils - createNumber() thows a StringIndexOutOfBoundsException when only an "l" is passed in. + NumberUtils - createNumber() throws a StringIndexOutOfBoundsException when only an "l" is passed in. NumberUtils - isNumber(String) and createNumber(String) both modified to support '2.'. StringUtils - improve handling of case-insensitive Strings. StringUtils - replaceEach() no longer NPEs when null appears in the last String[]. @@ -813,7 +1851,7 @@ The type attribute can be add,update,fix,remove. Extension to ClassUtils: Obtain the primitive class from a wrapper. Javadoc bugs - cannot find object. Optimize HashCodeBuilder.append(Object). - https://commons.apache.org/lang/developerguide.html "Building" section is incorrect and incomplete. + https://commons.apache.org/proper/commons-lang/developerguide.html "Building" section is incorrect and incomplete. Ambiguous / confusing names in StringUtils replace* methods. Add new splitByWholeSeparatorPreserveAllTokens() methods to StringUtils. Add getStartTime to StopWatch. @@ -825,7 +1863,7 @@ The type attribute can be add,update,fix,remove. Calculating A date fragment in any time-unit. Memory usage improvement for StringUtils#getLevenshteinDistance(). Add ExtendedMessageFormat to org.apache.commons.lang.text. - StringEscapeUtils.escapeJavaScript() method did not escape '/' into '\/', it will make IE render page uncorrectly. + StringEscapeUtils.escapeJavaScript() method did not escape '/' into '\/', it will make IE render page incorrectly. Add toArray() method to IntRange and LongRange classes. add SystemUtils.IS_OS_WINDOWS_VISTA field. Pointless synchronized in ThreadLocal.initialValue should be removed. @@ -836,9 +1874,9 @@ The type attribute can be add,update,fix,remove. - Use of enum prevents a classloader from being garbage collected resuling in out of memory exceptions. + Use of enum prevents a classloader from being garbage collected resulting in out of memory exceptions. NumberUtils.max(byte[]) and NumberUtils.min(byte[]) are missing. - Null-safe comparison methods for finding most recent / least recent dates. + Null-safe comparison methods for finding the most recent / least recent dates. StopWatch: suspend() acts as split(), if followed by stop(). StrBuilder.replaceAll and StrBuilder.deleteAll can throw ArrayIndexOutOfBoundsException. Bug in method appendFixedWidthPadRight of class StrBuilder causes an ArrayIndexOutOfBoundsException. @@ -868,7 +1906,7 @@ The type attribute can be add,update,fix,remove. EqualsBuilder.append(Object[], Object[]) crashes with a NullPointerException if an element of the first array is null. Serialization - not backwards compatible. Replace Clover with Cobertura. - ValuedEnum.compareTo(Object other) not typesafe - it easily could be... + ValuedEnum.compareTo(Object other) not typesafe - it easily could be.. LocaleUtils test fails under Mustang. Javadoc example for StringUtils.splitByWholeSeparator incorrect. PADDING array in StringUtils overflows on '\uffff'. @@ -928,7 +1966,7 @@ The type attribute can be add,update,fix,remove. New class proposal: CharacterEncoding. SystemUtils fails init on HP-UX. Javadoc - 'four basic XML entities' should be 5 (apos is missing). - o.a.c.lang.enum.ValuedEnum: 'enum'is a keyword in JDK1.5.0. + o.a.c.lang.enum.ValuedEnum: 'enum' is a keyword in JDK 1.5.0. StringEscapeUtils.unescapeHtml() doesn't handle an empty entity. EqualsBuilder.append(Object[], Object[]) incorrectly checks that rhs[i] is instance of lhs[i]'s class. Method enums.Enum.equals(Object o) doesn't work correctly. @@ -965,7 +2003,7 @@ The type attribute can be add,update,fix,remove. Add convenience format(long) methods to FastDateFormat. Enum's outer class may not be loaded for EnumUtils. WordUtils.capitalizeFully(String str) should take a delimiter. - Make Javadoc crosslinking configurable. + Make Javadoc cross-linking configurable. Minor Javadoc fixes for StringUtils.contains(String, String). Error in Javadoc for StringUtils.chomp(String, String). StringUtils.defaultString: Documentation error. @@ -1014,7 +2052,7 @@ The type attribute can be add,update,fix,remove. test.time fails in Japanese (non-us) locale. NumberUtils.isNumber allows illegal trailing characters. Improve Javadoc and overflow behavior of Fraction. - RandomStringUtils infloops with length > 1. + RandomStringUtils infinite loop with length > 1. test.lang fails if compiled with non iso-8859-1 locales. SystemUtils does not play nice in an Applet. time unit tests fail on Sundays. diff --git a/src/changes/release-notes.vm b/src/changes/release-notes.vm index 7b41e94899d..d8c2e9936c0 100644 --- a/src/changes/release-notes.vm +++ b/src/changes/release-notes.vm @@ -6,7 +6,7 @@ ## "License"); you may not use this file except in compliance ## with the License. You may obtain a copy of the License at ## -## http://www.apache.org/licenses/LICENSE-2.0 +## https://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, ## software distributed under the License is distributed on an @@ -23,7 +23,7 @@ The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 +https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -31,29 +31,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +${project.name} ${version} Release Notes +---------------------------------------- - ${project.name} - Version ${version} - Release Notes - - -INTRODUCTION: - -This document contains the release notes for the ${version} version of Apache Commons Lang. -Commons Lang is a set of utility functions and reusable components that should be of use in any -Java environment. - -Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8. - -For the advice on upgrading from 2.x to 3.x, see the following page: - - https://commons.apache.org/lang/article3_0.html +The ${developmentTeam} is pleased to announce the release of ${project.name} ${version}. $introduction.replaceAll("(? - - - - - - - - + + + + + diff --git a/src/conf/pmd-exclude.properties b/src/conf/pmd-exclude.properties new file mode 100644 index 00000000000..db235c6be27 --- /dev/null +++ b/src/conf/pmd-exclude.properties @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# We have ThreadGroup utilities +org.apache.commons.lang3.ThreadUtils = AvoidThreadGroup + +# TODO? +# violation beginline="2900" endline="2900" begincolumn="13" endcolumn="21" rule="AvoidBranchingStatementAsLastInLoop" ruleset="Error Prone" package="org.apache.commons.lang3" class="StringUtils" method="indexOfAnyBut" +org.apache.commons.lang3.StringUtils = AvoidBranchingStatementAsLastInLoop + +# Bug in PMD when the same class name exists in different packages +# Unnecessary use of fully qualified name 'org.apache.commons.lang3.function.FailableRunnable' due to existing same package import 'org.apache.commons.lang3.*' +org.apache.commons.lang3.Functions = UnnecessaryFullyQualifiedName diff --git a/src/conf/pmd-ruleset.xml b/src/conf/pmd-ruleset.xml new file mode 100644 index 00000000000..38d703ab4a9 --- /dev/null +++ b/src/conf/pmd-ruleset.xml @@ -0,0 +1,27 @@ + + + + Excludes from default PMD rules. + + + + + + + diff --git a/src/conf/spotbugs-exclude-filter-java8-sb-4.8.6.xml b/src/conf/spotbugs-exclude-filter-java8-sb-4.8.6.xml new file mode 100644 index 00000000000..4c5e832366f --- /dev/null +++ b/src/conf/spotbugs-exclude-filter-java8-sb-4.8.6.xml @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spotbugs-exclude-filter.xml b/src/conf/spotbugs-exclude-filter.xml similarity index 60% rename from spotbugs-exclude-filter.xml rename to src/conf/spotbugs-exclude-filter.xml index 86368909d43..a07ceb9e903 100644 --- a/spotbugs-exclude-filter.xml +++ b/src/conf/spotbugs-exclude-filter.xml @@ -7,7 +7,7 @@ (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,49 @@ false positive nature has been analyzed individually and they have been put here to instruct findbugs it must ignore them. --> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -71,20 +113,35 @@ + + + + + + + + + + - + - - + + + + + + + @@ -101,7 +158,7 @@ + in switch statements. All the excluded methods have switch statements that contain a default case. --> @@ -125,8 +182,13 @@ + + + + + - + @@ -151,6 +213,28 @@ - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/apache/commons/lang3/AnnotationUtils.java b/src/main/java/org/apache/commons/lang3/AnnotationUtils.java index bc51037c7c2..bdaf76433ca 100644 --- a/src/main/java/org/apache/commons/lang3/AnnotationUtils.java +++ b/src/main/java/org/apache/commons/lang3/AnnotationUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,15 +17,16 @@ package org.apache.commons.lang3; import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import org.apache.commons.lang3.builder.AbstractReflection; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.commons.lang3.exception.UncheckedException; /** - *

Helper methods for working with {@link Annotation} instances.

+ * Helper methods for working with {@link Annotation} instances. * *

This class contains various utility methods that make working with * annotations simpler.

@@ -48,6 +49,7 @@ public class AnnotationUtils { * A style that prints annotations as recommended. */ private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() { + /** Serialization version */ private static final long serialVersionUID = 1L; @@ -68,46 +70,127 @@ public class AnnotationUtils { * {@inheritDoc} */ @Override - protected String getShortClassName(final Class cls) { - for (final Class iface : ClassUtils.getAllInterfaces(cls)) { - if (Annotation.class.isAssignableFrom(iface)) { - return "@" + iface.getName(); - } + protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) { + if (value instanceof Annotation) { + value = AnnotationUtils.toString((Annotation) value); } - return StringUtils.EMPTY; + super.appendDetail(buffer, fieldName, value); } /** * {@inheritDoc} */ @Override - protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) { - if (value instanceof Annotation) { - value = AnnotationUtils.toString((Annotation) value); - } - super.appendDetail(buffer, fieldName, value); + protected String getShortClassName(final Class cls) { + // formatter:off + return ClassUtils.getAllInterfaces(cls).stream().filter(Annotation.class::isAssignableFrom).findFirst() + .map(iface -> "@" + iface.getName()) + .orElse(StringUtils.EMPTY); + // formatter:on } }; /** - *

{@code AnnotationUtils} instances should NOT be constructed in - * standard programming. Instead, the class should be used statically.

+ * Helper method for comparing two arrays of annotations. * - *

This constructor is public to permit tools that require a JavaBean - * instance to operate.

+ * @param a1 The first array + * @param a2 The second array + * @return A flag whether these arrays are equal */ - public AnnotationUtils() { + private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) { + if (a1.length != a2.length) { + return false; + } + for (int i = 0; i < a1.length; i++) { + if (!equals(a1[i], a2[i])) { + return false; + } + } + return true; } - //----------------------------------------------------------------------- /** - *

Checks if two annotations are equal using the criteria for equality - * presented in the {@link Annotation#equals(Object)} API docs.

+ * Helper method for comparing two objects of an array type. * - * @param a1 the first Annotation to compare, {@code null} returns + * @param componentType The component type of the array + * @param o1 The first object + * @param o2 The second object + * @return A flag whether these objects are equal + */ + private static boolean arrayMemberEquals(final Class componentType, final Object o1, final Object o2) { + if (componentType.isAnnotation()) { + return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2); + } + if (componentType.equals(Byte.TYPE)) { + return Arrays.equals((byte[]) o1, (byte[]) o2); + } + if (componentType.equals(Short.TYPE)) { + return Arrays.equals((short[]) o1, (short[]) o2); + } + if (componentType.equals(Integer.TYPE)) { + return Arrays.equals((int[]) o1, (int[]) o2); + } + if (componentType.equals(Character.TYPE)) { + return Arrays.equals((char[]) o1, (char[]) o2); + } + if (componentType.equals(Long.TYPE)) { + return Arrays.equals((long[]) o1, (long[]) o2); + } + if (componentType.equals(Float.TYPE)) { + return Arrays.equals((float[]) o1, (float[]) o2); + } + if (componentType.equals(Double.TYPE)) { + return Arrays.equals((double[]) o1, (double[]) o2); + } + if (componentType.equals(Boolean.TYPE)) { + return Arrays.equals((boolean[]) o1, (boolean[]) o2); + } + return Arrays.equals((Object[]) o1, (Object[]) o2); + } + + /** + * Helper method for generating a hash code for an array. + * + * @param componentType The component type of the array + * @param o The array + * @return A hash code for the specified array + */ + private static int arrayMemberHash(final Class componentType, final Object o) { + if (componentType.equals(Byte.TYPE)) { + return Arrays.hashCode((byte[]) o); + } + if (componentType.equals(Short.TYPE)) { + return Arrays.hashCode((short[]) o); + } + if (componentType.equals(Integer.TYPE)) { + return Arrays.hashCode((int[]) o); + } + if (componentType.equals(Character.TYPE)) { + return Arrays.hashCode((char[]) o); + } + if (componentType.equals(Long.TYPE)) { + return Arrays.hashCode((long[]) o); + } + if (componentType.equals(Float.TYPE)) { + return Arrays.hashCode((float[]) o); + } + if (componentType.equals(Double.TYPE)) { + return Arrays.hashCode((double[]) o); + } + if (componentType.equals(Boolean.TYPE)) { + return Arrays.hashCode((boolean[]) o); + } + return Arrays.hashCode((Object[]) o); + } + + /** + * Checks if two annotations are equal using the criteria for equality + * presented in the {@link Annotation#equals(Object)} API docs. + * + * @param a1 The first Annotation to compare, {@code null} returns * {@code false} unless both are {@code null} - * @param a2 the second Annotation to compare, {@code null} returns + * @param a2 The second Annotation to compare, {@code null} returns * {@code false} unless both are {@code null} * @return {@code true} if the two annotations are {@code equal} or both * {@code null} @@ -130,6 +213,7 @@ public static boolean equals(final Annotation a1, final Annotation a2) { for (final Method m : type1.getDeclaredMethods()) { if (m.getParameterTypes().length == 0 && isValidAnnotationMemberType(m.getReturnType())) { + AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m); final Object v1 = m.invoke(a1); final Object v2 = m.invoke(a2); if (!memberEquals(m.getReturnType(), v1, v2)) { @@ -137,20 +221,20 @@ && isValidAnnotationMemberType(m.getReturnType())) { } } } - } catch (final IllegalAccessException | InvocationTargetException ex) { - return false; + } catch (final ReflectiveOperationException ex) { + throw new IllegalStateException(ex); } return true; } /** - *

Generate a hash code for the given annotation using the algorithm - * presented in the {@link Annotation#hashCode()} API docs.

+ * Generate a hash code for the given annotation using the algorithm + * presented in the {@link Annotation#hashCode()} API docs. * - * @param a the Annotation for a hash code calculation is desired, not + * @param a The Annotation for a hash code calculation is desired, not * {@code null} - * @return the calculated hash code - * @throws RuntimeException if an {@code Exception} is encountered during + * @return The calculated hash code + * @throws RuntimeException if an {@link Exception} is encountered during * annotation member access * @throws IllegalStateException if an annotation method invocation returns * {@code null} @@ -160,55 +244,47 @@ public static int hashCode(final Annotation a) { final Class type = a.annotationType(); for (final Method m : type.getDeclaredMethods()) { try { + AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m); final Object value = m.invoke(a); if (value == null) { - throw new IllegalStateException( - String.format("Annotation method %s returned null", m)); + throw new IllegalStateException(String.format("Annotation method %s returned null", m)); } result += hashMember(m.getName(), value); - } catch (final RuntimeException ex) { - throw ex; - } catch (final Exception ex) { - throw new RuntimeException(ex); + } catch (final ReflectiveOperationException ex) { + throw new UncheckedException(ex); } } return result; } + //besides modularity, this has the advantage of autoboxing primitives: /** - *

Generate a string representation of an Annotation, as suggested by - * {@link Annotation#toString()}.

+ * Helper method for generating a hash code for a member of an annotation. * - * @param a the annotation of which a string representation is desired - * @return the standard string representation of an annotation, not - * {@code null} + * @param name The name of the member + * @param value The value of the member + * @return A hash code for this member */ - public static String toString(final Annotation a) { - final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE); - for (final Method m : a.annotationType().getDeclaredMethods()) { - if (m.getParameterTypes().length > 0) { - continue; //wtf? - } - try { - builder.append(m.getName(), m.invoke(a)); - } catch (final RuntimeException ex) { - throw ex; - } catch (final Exception ex) { - throw new RuntimeException(ex); - } + private static int hashMember(final String name, final Object value) { + final int part1 = name.hashCode() * 127; + if (ObjectUtils.isArray(value)) { + return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value); } - return builder.build(); + if (value instanceof Annotation) { + return part1 ^ hashCode((Annotation) value); + } + return part1 ^ value.hashCode(); } /** - *

Checks if the specified type is permitted as an annotation member.

+ * Checks if the specified type is permitted as an annotation member. * *

The Java language specification only permits certain types to be used * in annotations. These include {@link String}, {@link Class}, primitive * types, {@link Annotation}, {@link Enum}, and single-dimensional arrays of * these types.

* - * @param type the type to check, {@code null} + * @param type The type to check, {@code null} * @return {@code true} if the type is a valid type to use in an annotation */ public static boolean isValidAnnotationMemberType(Class type) { @@ -222,34 +298,15 @@ public static boolean isValidAnnotationMemberType(Class type) { || String.class.equals(type) || Class.class.equals(type); } - //besides modularity, this has the advantage of autoboxing primitives: - /** - * Helper method for generating a hash code for a member of an annotation. - * - * @param name the name of the member - * @param value the value of the member - * @return a hash code for this member - */ - private static int hashMember(final String name, final Object value) { - final int part1 = name.hashCode() * 127; - if (value.getClass().isArray()) { - return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value); - } - if (value instanceof Annotation) { - return part1 ^ hashCode((Annotation) value); - } - return part1 ^ value.hashCode(); - } - /** * Helper method for checking whether two objects of the given type are * equal. This method is used to compare the parameters of two annotation * instances. * - * @param type the type of the objects to be compared - * @param o1 the first object - * @param o2 the second object - * @return a flag whether these objects are equal + * @param type The type of the objects to be compared + * @param o1 The first object + * @param o2 The second object + * @return A flag whether these objects are equal */ private static boolean memberEquals(final Class type, final Object o1, final Object o2) { if (o1 == o2) { @@ -268,95 +325,40 @@ private static boolean memberEquals(final Class type, final Object o1, final } /** - * Helper method for comparing two objects of an array type. - * - * @param componentType the component type of the array - * @param o1 the first object - * @param o2 the second object - * @return a flag whether these objects are equal - */ - private static boolean arrayMemberEquals(final Class componentType, final Object o1, final Object o2) { - if (componentType.isAnnotation()) { - return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2); - } - if (componentType.equals(Byte.TYPE)) { - return Arrays.equals((byte[]) o1, (byte[]) o2); - } - if (componentType.equals(Short.TYPE)) { - return Arrays.equals((short[]) o1, (short[]) o2); - } - if (componentType.equals(Integer.TYPE)) { - return Arrays.equals((int[]) o1, (int[]) o2); - } - if (componentType.equals(Character.TYPE)) { - return Arrays.equals((char[]) o1, (char[]) o2); - } - if (componentType.equals(Long.TYPE)) { - return Arrays.equals((long[]) o1, (long[]) o2); - } - if (componentType.equals(Float.TYPE)) { - return Arrays.equals((float[]) o1, (float[]) o2); - } - if (componentType.equals(Double.TYPE)) { - return Arrays.equals((double[]) o1, (double[]) o2); - } - if (componentType.equals(Boolean.TYPE)) { - return Arrays.equals((boolean[]) o1, (boolean[]) o2); - } - return Arrays.equals((Object[]) o1, (Object[]) o2); - } - - /** - * Helper method for comparing two arrays of annotations. + * Generate a string representation of an Annotation, as suggested by + * {@link Annotation#toString()}. * - * @param a1 the first array - * @param a2 the second array - * @return a flag whether these arrays are equal + * @param a The annotation of which a string representation is desired + * @return The standard string representation of an annotation, not + * {@code null} */ - private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) { - if (a1.length != a2.length) { - return false; - } - for (int i = 0; i < a1.length; i++) { - if (!equals(a1[i], a2[i])) { - return false; + public static String toString(final Annotation a) { + final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE); + for (final Method m : a.annotationType().getDeclaredMethods()) { + if (m.getParameterTypes().length > 0) { + continue; // what? + } + try { + AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m); + builder.append(m.getName(), m.invoke(a)); + } catch (final ReflectiveOperationException ex) { + throw new UncheckedException(ex); } } - return true; + return builder.build(); } /** - * Helper method for generating a hash code for an array. + * {@link AnnotationUtils} instances should NOT be constructed in + * standard programming. Instead, the class should be used statically. + * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

* - * @param componentType the component type of the array - * @param o the array - * @return a hash code for the specified array + * @deprecated TODO Make private in 4.0. */ - private static int arrayMemberHash(final Class componentType, final Object o) { - if (componentType.equals(Byte.TYPE)) { - return Arrays.hashCode((byte[]) o); - } - if (componentType.equals(Short.TYPE)) { - return Arrays.hashCode((short[]) o); - } - if (componentType.equals(Integer.TYPE)) { - return Arrays.hashCode((int[]) o); - } - if (componentType.equals(Character.TYPE)) { - return Arrays.hashCode((char[]) o); - } - if (componentType.equals(Long.TYPE)) { - return Arrays.hashCode((long[]) o); - } - if (componentType.equals(Float.TYPE)) { - return Arrays.hashCode((float[]) o); - } - if (componentType.equals(Double.TYPE)) { - return Arrays.hashCode((double[]) o); - } - if (componentType.equals(Boolean.TYPE)) { - return Arrays.hashCode((boolean[]) o); - } - return Arrays.hashCode((Object[]) o); + @Deprecated + public AnnotationUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/AppendableJoiner.java b/src/main/java/org/apache/commons/lang3/AppendableJoiner.java new file mode 100644 index 00000000000..24b7d1f7022 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/AppendableJoiner.java @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.io.IOException; +import java.util.Iterator; +import java.util.StringJoiner; +import java.util.function.Supplier; + +import org.apache.commons.lang3.exception.UncheckedException; +import org.apache.commons.lang3.function.FailableBiConsumer; + +/** + * Joins an array or {@link Iterable} into an existing {@link Appendable} like a {@link StringBuilder}; with the goal for call sites to avoid creating + * intermediary Strings. This is like {@link String#join(CharSequence, CharSequence...)}, {@link String#join(CharSequence, Iterable)}, and {@link StringJoiner}. + *

+ * Keep an instance in a (static) variable for efficient joining into an {@link Appendable} or {@link StringBuilder} without creating temporary Strings. + *

+ *

+ * Use the builder and instance methods to reuse the same kind of joining prefix, suffix, delimiter, and string conversion. + *

+ *

+ * For example: + *

+ * + *
{@code
+ * // A reuseable instance
+ * private static final AppendableJoiner JOINER = AppendableJoiner.builder()
+ *     .setPrefix("[")
+ *     .setSuffix("]")
+ *     .setDelimiter(", ")
+ *     .get();
+ * }
+ * ...
+ * // Builds straight into a StringBuilder:
+ * StringBuilder sbuilder = new StringBuilder("1");
+ * JOINER.join(sbuilder, "A", "B");
+ * sbuilder.append("2");
+ * JOINER.join(sbuilder, "C", "D");
+ * sbuilder.append("3");
+ * // Returns "1[A, B]2[C, D]3"
+ * return sbuilder.toString();
+ * }
+ * 

+ * To provide a custom Object element to {@link CharSequence} converter, call {@link Builder#setElementAppender(FailableBiConsumer)}, for example: + *

+ * + *
{@code
+ * private static final AppendableJoiner JOINER = AppendableJoiner.builder()
+ *     .setElementAppender(e -> (a, e) -> a.append(e.getFoo())
+ *                                        a.append(e.getBar())
+ *                                        a.append('!'))
+ *     ...
+ *     .get();
+ * }
+ * }
+ *

+ * This class is immutable and thread-safe. + *

+ * + * @param The type of elements to join. + * @see Appendable + * @see StringBuilder + * @see String#join(CharSequence, CharSequence...) + * @see String#join(CharSequence, Iterable) + * @see StringJoiner + * @since 3.15.0 + */ +public final class AppendableJoiner { + + /** + * Builds instances of {@link AppendableJoiner}. + * + * @param The type of elements to join. + */ + public static final class Builder implements Supplier> { + + /** The sequence of characters to be used at the beginning. */ + private CharSequence prefix; + + /** The sequence of characters to be used at the end. */ + private CharSequence suffix; + + /** The delimiter that separates each element. */ + private CharSequence delimiter; + + /** The consumer used to render each element of type {@code T} onto an {@link Appendable}. */ + private FailableBiConsumer appender; + + /** + * Constructs a new instance. + */ + Builder() { + // empty + } + + /** + * Gets a new instance of {@link AppendableJoiner}. + */ + @Override + public AppendableJoiner get() { + return new AppendableJoiner<>(prefix, suffix, delimiter, appender); + } + + /** + * Sets the delimiter that separates each element. + * + * @param delimiter The delimiter that separates each element. + * @return {@code this} instance. + */ + public Builder setDelimiter(final CharSequence delimiter) { + this.delimiter = delimiter; + return this; + } + + /** + * Sets the consumer used to render each element of type {@code T} onto an {@link Appendable}. + * + * @param appender The consumer used to render each element of type {@code T} onto an {@link Appendable}. + * @return {@code this} instance. + */ + public Builder setElementAppender(final FailableBiConsumer appender) { + this.appender = appender; + return this; + } + + /** + * Sets the sequence of characters to be used at the beginning. + * + * @param prefix The sequence of characters to be used at the beginning. + * @return {@code this} instance. + */ + public Builder setPrefix(final CharSequence prefix) { + this.prefix = prefix; + return this; + } + + /** + * Sets the sequence of characters to be used at the end. + * + * @param suffix The sequence of characters to be used at the end. + * @return {@code this} instance. + */ + public Builder setSuffix(final CharSequence suffix) { + this.suffix = suffix; + return this; + } + + } + + /** + * Creates a new builder. + * + * @param The type of elements. + * @return A new builder. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** Could be public in the future, in some form. */ + @SafeVarargs + static A joinA(final A appendable, final CharSequence prefix, final CharSequence suffix, final CharSequence delimiter, + final FailableBiConsumer appender, final T... elements) throws IOException { + return joinArray(appendable, prefix, suffix, delimiter, appender, elements); + } + + private static A joinArray(final A appendable, final CharSequence prefix, final CharSequence suffix, final CharSequence delimiter, + final FailableBiConsumer appender, final T[] elements) throws IOException { + appendable.append(prefix); + if (elements != null) { + if (elements.length > 0) { + appender.accept(appendable, elements[0]); + } + for (int i = 1; i < elements.length; i++) { + appendable.append(delimiter); + appender.accept(appendable, elements[i]); + } + } + appendable.append(suffix); + return appendable; + } + + /** Could be public in the future, in some form. */ + static StringBuilder joinI(final StringBuilder stringBuilder, final CharSequence prefix, final CharSequence suffix, final CharSequence delimiter, + final FailableBiConsumer appender, final Iterable elements) { + try { + return joinIterable(stringBuilder, prefix, suffix, delimiter, appender, elements); + } catch (final IOException e) { + // Cannot happen with a StringBuilder. + throw new UncheckedException(e); + } + } + + private static A joinIterable(final A appendable, final CharSequence prefix, final CharSequence suffix, + final CharSequence delimiter, final FailableBiConsumer appender, final Iterable elements) throws IOException { + appendable.append(prefix); + if (elements != null) { + final Iterator iterator = elements.iterator(); + if (iterator.hasNext()) { + appender.accept(appendable, iterator.next()); + } + while (iterator.hasNext()) { + appendable.append(delimiter); + appender.accept(appendable, iterator.next()); + } + } + appendable.append(suffix); + return appendable; + } + + /** Could be public in the future, in some form. */ + @SafeVarargs + static StringBuilder joinSB(final StringBuilder stringBuilder, final CharSequence prefix, final CharSequence suffix, final CharSequence delimiter, + final FailableBiConsumer appender, final T... elements) { + try { + return joinArray(stringBuilder, prefix, suffix, delimiter, appender, elements); + } catch (final IOException e) { + // Cannot happen with a StringBuilder. + throw new UncheckedException(e); + } + } + + private static CharSequence nonNull(final CharSequence value) { + return value != null ? value : StringUtils.EMPTY; + } + + /** The sequence of characters to be used at the beginning. */ + private final CharSequence prefix; + + /** The sequence of characters to be used at the end. */ + private final CharSequence suffix; + + /** The delimiter that separates each element. */ + private final CharSequence delimiter; + + private final FailableBiConsumer appender; + + /** + * Constructs a new instance. + */ + private AppendableJoiner(final CharSequence prefix, final CharSequence suffix, final CharSequence delimiter, + final FailableBiConsumer appender) { + this.prefix = nonNull(prefix); + this.suffix = nonNull(suffix); + this.delimiter = nonNull(delimiter); + this.appender = appender != null ? appender : (a, e) -> a.append(String.valueOf(e)); + } + + /** + * Joins stringified objects from the given Iterable into a StringBuilder. + * + * @param stringBuilder The target. + * @param elements The source. + * @return The given StringBuilder. + */ + public StringBuilder join(final StringBuilder stringBuilder, final Iterable elements) { + return joinI(stringBuilder, prefix, suffix, delimiter, appender, elements); + } + + /** + * Joins stringified objects from the given array into a StringBuilder. + * + * @param stringBuilder The target. + * @param elements The source. + * @return The given target StringBuilder. + */ + public StringBuilder join(final StringBuilder stringBuilder, @SuppressWarnings("unchecked") final T... elements) { + return joinSB(stringBuilder, prefix, suffix, delimiter, appender, elements); + } + + /** + * Joins stringified objects from the given Iterable into an Appendable. + * + * @param the Appendable type. + * @param appendable The target. + * @param elements The source. + * @return The given StringBuilder. + * @throws IOException If an I/O error occurs + */ + public A joinA(final A appendable, final Iterable elements) throws IOException { + return joinIterable(appendable, prefix, suffix, delimiter, appender, elements); + } + + /** + * Joins stringified objects from the given array into an Appendable. + * + * @param the Appendable type. + * @param appendable The target. + * @param elements The source. + * @return The given StringBuilder. + * @throws IOException If an I/O error occurs + */ + public A joinA(final A appendable, @SuppressWarnings("unchecked") final T... elements) throws IOException { + return joinA(appendable, prefix, suffix, delimiter, appender, elements); + } + +} diff --git a/src/main/java/org/apache/commons/lang3/ArchUtils.java b/src/main/java/org/apache/commons/lang3/ArchUtils.java index e0b2aae8cab..1ccb688436a 100644 --- a/src/main/java/org/apache/commons/lang3/ArchUtils.java +++ b/src/main/java/org/apache/commons/lang3/ArchUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,19 +16,18 @@ */ package org.apache.commons.lang3; -import org.apache.commons.lang3.arch.Processor; - import java.util.HashMap; import java.util.Map; -import java.util.stream.Stream; + +import org.apache.commons.lang3.arch.Processor; +import org.apache.commons.lang3.stream.Streams; /** - * An utility class for the os.arch System Property. The class defines methods for - * identifying the architecture of the current JVM. + * Provides methods for identifying the architecture of the current JVM based on the {@code "os.arch"} system property. *

- * Important: The os.arch System Property returns the architecture used by the JVM - * not of the operating system. + * Important: The {@code "os.arch"} system property returns the architecture used by the JVM not of the operating system. *

+ * * @since 3.6 */ public class ArchUtils { @@ -40,49 +39,10 @@ public class ArchUtils { init(); } - private static void init() { - init_X86_32Bit(); - init_X86_64Bit(); - init_IA64_32Bit(); - init_IA64_64Bit(); - init_PPC_32Bit(); - init_PPC_64Bit(); - } - - private static void init_X86_32Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.X86); - addProcessors(processor, "x86", "i386", "i486", "i586", "i686", "pentium"); - } - - private static void init_X86_64Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.X86); - addProcessors(processor, "x86_64", "amd64", "em64t", "universal"); - } - - private static void init_IA64_32Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.IA_64); - addProcessors(processor, "ia64_32", "ia64n"); - } - - private static void init_IA64_64Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.IA_64); - addProcessors(processor, "ia64", "ia64w"); - } - - private static void init_PPC_32Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.PPC); - addProcessors(processor, "ppc", "power", "powerpc", "power_pc", "power_rs"); - } - - private static void init_PPC_64Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.PPC); - addProcessors(processor, "ppc64", "power64", "powerpc64", "power_pc64", "power_rs64"); - } - /** * Adds the given {@link Processor} with the given key {@link String} to the map. * - * @param key The key as {@link String}. + * @param key The key as {@link String}. * @param processor The {@link Processor} to add. * @throws IllegalStateException If the key already exists. */ @@ -96,37 +56,94 @@ private static void addProcessor(final String key, final Processor processor) { /** * Adds the given {@link Processor} with the given keys to the map. * - * @param keys The keys. + * @param keys The keys. * @param processor The {@link Processor} to add. * @throws IllegalStateException If the key already exists. */ private static void addProcessors(final Processor processor, final String... keys) { - Stream.of(keys).forEach(e -> addProcessor(e, processor)); + Streams.of(keys).forEach(e -> addProcessor(e, processor)); } /** - * Returns a {@link Processor} object of the current JVM. + * Gets a {@link Processor} object of the current JVM. * *

- * Important: The os.arch System Property returns the architecture used by the JVM - * not of the operating system. + * Important: The {@code "os.arch"} system property returns the architecture used by the JVM not of the operating system. *

* * @return A {@link Processor} when supported, else {@code null}. */ public static Processor getProcessor() { - return getProcessor(SystemUtils.OS_ARCH); + return getProcessor(SystemProperties.getOsArch()); } /** - * Returns a {@link Processor} object the given value {@link String}. The {@link String} must be - * like a value returned by the os.arch System Property. + * Gets a {@link Processor} object the given value {@link String}. The {@link String} must be like a value returned by the {@code "os.arch"} system + * property. * - * @param value A {@link String} like a value returned by the os.arch System Property. + * @param value A {@link String} like a value returned by the {@code os.arch} System Property. * @return A {@link Processor} when it exists, else {@code null}. */ public static Processor getProcessor(final String value) { return ARCH_TO_PROCESSOR.get(value); } + private static void init() { + init_X86_32Bit(); + init_X86_64Bit(); + init_IA64_32Bit(); + init_IA64_64Bit(); + init_PPC_32Bit(); + init_PPC_64Bit(); + init_Aarch_64Bit(); + init_RISCV_32Bit(); + init_RISCV_64Bit(); + } + + private static void init_Aarch_64Bit() { + addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.AARCH_64), "aarch64"); + } + + private static void init_IA64_32Bit() { + addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.IA_64), "ia64_32", "ia64n"); + } + + private static void init_IA64_64Bit() { + addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.IA_64), "ia64", "ia64w"); + } + + private static void init_PPC_32Bit() { + addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.PPC), "ppc", "power", "powerpc", "power_pc", "power_rs"); + } + + private static void init_PPC_64Bit() { + addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.PPC), "ppc64", "ppc64le", "power64", "powerpc64", "power_pc64", "power_rs64"); + } + + private static void init_RISCV_32Bit() { + addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.RISC_V), "riscv32"); + } + + private static void init_RISCV_64Bit() { + addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.RISC_V), "riscv64"); + } + + private static void init_X86_32Bit() { + addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.X86), "x86", "i386", "i486", "i586", "i686", "pentium"); + } + + private static void init_X86_64Bit() { + addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.X86), "x86_64", "amd64", "em64t", "universal"); + } + + /** + * Make private in 4.0. + * + * @deprecated TODO Make private in 4.0. + */ + @Deprecated + public ArchUtils() { + // empty + } + } diff --git a/src/main/java/org/apache/commons/lang3/ArrayFill.java b/src/main/java/org/apache/commons/lang3/ArrayFill.java new file mode 100644 index 00000000000..3cf7e9e34d8 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/ArrayFill.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.util.Arrays; +import java.util.function.IntFunction; + +import org.apache.commons.lang3.function.FailableIntFunction; + +/** + * Fills and returns arrays in the fluent style. + * + * @since 3.14.0 + */ +public final class ArrayFill { + + /** + * Fills and returns the given array, assigning {@code 0} to each element of the array. + *
+     * ArrayFill.fill(a, (byte) 0);
+     * 
+ * + * @param a The array to fill (may be null). + * @return The given array. + * @see Arrays#fill(byte[],byte) + * @see ArrayFill#fill(byte[],byte) + * @since 3.21.0 + */ + public static byte[] clear(final byte[] a) { + return fill(a, (byte) 0); + } + + /** + * Fills and returns the given array, assigning {@code '\0'} to each element of the array. + *

+ * Equivalent to: + *

+ *
+     * ArrayFill.fill(a, '\0'); // and not '0'!
+     * 
+ * + * @param a The array to fill (may be null). + * @return The given array. + * @see Arrays#fill(char[],char) + * @see ArrayFill#fill(char[],char) + * @see CharUtils#NUL + * @since 3.21.0 + */ + public static char[] clear(final char[] a) { + return fill(a, CharUtils.NUL); // and not '0'! + } + + /** + * Fills and returns the given array, assigning {@code '\0'} to each element of the array. + *

+ * Equivalent to: + *

+ *
+     * ArrayFill.fill(a, fromIndex, toIndex, '\0'); // and not '0'!
+     * 
+ * + * @param a The array to fill (may be null). + * @param fromIndex The index of the first element (inclusive) to be filled with {@code '\0'}. + * @param toIndex The index of the last element (exclusive) to be filled with {@code '\0'}. + * @return The given array. + * @see Arrays#fill(char[], int, int, char) + * @since 3.21.0 + */ + public static char[] clear(final char[] a, final int fromIndex, final int toIndex) { + return fill(a, fromIndex, toIndex, CharUtils.NUL); + } + + /** + * Fills and returns the given array, assigning the given {@code boolean} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(boolean[],boolean) + * @since 3.18.0 + */ + public static boolean[] fill(final boolean[] a, final boolean val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code byte} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(byte[],byte) + */ + public static byte[] fill(final byte[] a, final byte val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code char} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(char[],char) + */ + public static char[] fill(final char[] a, final char val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code char} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @param fromIndex The index of the first element (inclusive) to be filled with the specified value. + * @param toIndex The index of the last element (exclusive) to be filled with the specified value. + * @return The given array. + * @see Arrays#fill(char[], int, int, char) + * @since 3.21.0 + */ + public static char[] fill(final char[] a, final int fromIndex, final int toIndex, final char val) { + if (a != null) { + Arrays.fill(a, fromIndex, toIndex, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code double} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(double[],double) + */ + public static double[] fill(final double[] a, final double val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code float} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(float[],float) + */ + public static float[] fill(final float[] a, final float val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code int} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(int[],int) + */ + public static int[] fill(final int[] a, final int val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code long} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(long[],long) + */ + public static long[] fill(final long[] a, final long val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, assigning the given {@code short} value to each element of the array. + * + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(short[],short) + */ + public static short[] fill(final short[] a, final short val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + /** + * Fills and returns the given array, using the provided generator function to compute each element. Like {@link Arrays#setAll(Object[], IntFunction)} with + * exception support. + *

+ * If the generator function throws an exception, it is relayed to the caller and the array is left in an indeterminate state. + *

+ *

+ * If the input array or generator function is null, nothing happens. + *

+ * + * @param type of elements of the array. + * @param array array to fill (may be null). + * @param generator A function accepting an index and producing the desired value for that position. + * @return The input array. + * @param The kind of thrown exception or error. + * @throws E Thrown by the given {@code generator}. + * @see Arrays#setAll(Object[], IntFunction) + * @since 3.18.0 + */ + public static T[] fill(final T[] array, final FailableIntFunction generator) throws E { + if (array != null && generator != null) { + for (int i = 0; i < array.length; i++) { + array[i] = generator.apply(i); + } + } + return array; + } + + /** + * Fills and returns the given array, assigning the given {@code T} value to each element of the array. + * + * @param The array type. + * @param a The array to fill (may be null). + * @param val The value to store in all elements of the array. + * @return The given array. + * @see Arrays#fill(Object[],Object) + */ + public static T[] fill(final T[] a, final T val) { + if (a != null) { + Arrays.fill(a, val); + } + return a; + } + + private ArrayFill() { + // no instances + } + +} diff --git a/src/main/java/org/apache/commons/lang3/ArraySorter.java b/src/main/java/org/apache/commons/lang3/ArraySorter.java new file mode 100644 index 00000000000..e381ba20339 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/ArraySorter.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * Sorts and returns arrays in the fluent style. + * + * TODO For 4.0, rename to ArraySort, since we cover the sort() method here, see also ArrayFill. + * + * @since 3.12.0 + */ +public class ArraySorter { + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(byte[]) + */ + public static byte[] sort(final byte[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(char[]) + */ + public static char[] sort(final char[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(double[]) + */ + public static double[] sort(final double[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(float[]) + */ + public static float[] sort(final float[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(int[]) + */ + public static int[] sort(final int[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(long[]) + */ + public static long[] sort(final long[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(short[]) + */ + public static short[] sort(final short[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param The array type. + * @param array The array to sort (may be null). + * @return The given array. + * @see Arrays#sort(Object[]) + */ + public static T[] sort(final T[] array) { + if (array != null) { + Arrays.sort(array); + } + return array; + } + + /** + * Sorts the given array into ascending order and returns it. + * + * @param The array type. + * @param array The array to sort (may be null). + * @param comparator The comparator to determine the order of the array. A {@code null} value uses the elements' + * {@link Comparable natural ordering}. + * @return The given array. + * @see Arrays#sort(Object[]) + */ + public static T[] sort(final T[] array, final Comparator comparator) { + if (array != null) { + Arrays.sort(array, comparator); + } + return array; + } + + /** + * Constructs a new instance. + * + * @deprecated Will be removed in 4.0.0. + */ + @Deprecated + public ArraySorter() { + // empty + } + +} diff --git a/src/main/java/org/apache/commons/lang3/ArrayUtils.java b/src/main/java/org/apache/commons/lang3/ArrayUtils.java index 3cfe46ea993..021e6d2d31a 100644 --- a/src/main/java/org/apache/commons/lang3/ArrayUtils.java +++ b/src/main/java/org/apache/commons/lang3/ArrayUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -20,156 +20,177 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.security.SecureRandom; import java.util.Arrays; import java.util.BitSet; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.function.FailableFunction; import org.apache.commons.lang3.mutable.MutableInt; +import org.apache.commons.lang3.stream.IntStreams; +import org.apache.commons.lang3.stream.Streams; /** - *

Operations on arrays, primitive arrays (like {@code int[]}) and + * Operations on arrays, primitive arrays (like {@code int[]}) and * primitive wrapper arrays (like {@code Integer[]}). - * - *

This class tries to handle {@code null} input gracefully. + *

+ * This class tries to handle {@code null} input gracefully. * An exception will not be thrown for a {@code null} * array input. However, an Object array that contains a {@code null} - * element may throw an exception. Each method documents its behaviour. + * element may throw an exception. Each method documents its behavior. + *

+ *

+ * #ThreadSafe# + *

* - *

#ThreadSafe# * @since 2.0 */ public class ArrayUtils { + /** + * Bridge class to {@link Math} methods for testing purposes. + */ + static class MathBridge { + static int addExact(final int a, final int b) { + return Math.addExact(a, b); + } + } + /** * An empty immutable {@code boolean} array. */ - public static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; + public static final boolean[] EMPTY_BOOLEAN_ARRAY = {}; /** - * An empty immutable {@code Boolean} array. + * An empty immutable {@link Boolean} array. */ - public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0]; + public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = {}; /** * An empty immutable {@code byte} array. */ - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + public static final byte[] EMPTY_BYTE_ARRAY = {}; /** - * An empty immutable {@code Byte} array. + * An empty immutable {@link Byte} array. */ - public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0]; + public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = {}; /** * An empty immutable {@code char} array. */ - public static final char[] EMPTY_CHAR_ARRAY = new char[0]; + public static final char[] EMPTY_CHAR_ARRAY = {}; /** - * An empty immutable {@code Character} array. + * An empty immutable {@link Character} array. */ - public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0]; + public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = {}; /** - * An empty immutable {@code Class} array. + * An empty immutable {@link Class} array. */ - public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + public static final Class[] EMPTY_CLASS_ARRAY = {}; /** * An empty immutable {@code double} array. */ - public static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; - - /** - * An empty immutable {@code Double} array. - */ - public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0]; + public static final double[] EMPTY_DOUBLE_ARRAY = {}; /** - * An empty immutable {@code Field} array. - * - * @since 3.10 + * An empty immutable {@link Double} array. */ - public static final Field[] EMPTY_FIELD_ARRAY = new Field[0]; + public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = {}; /** - * An empty immutable {@code Method} array. + * An empty immutable {@link Field} array. * * @since 3.10 */ - public static final Method[] EMPTY_METHOD_ARRAY = new Method[0]; + public static final Field[] EMPTY_FIELD_ARRAY = {}; /** * An empty immutable {@code float} array. */ - public static final float[] EMPTY_FLOAT_ARRAY = new float[0]; + public static final float[] EMPTY_FLOAT_ARRAY = {}; /** - * An empty immutable {@code Float} array. + * An empty immutable {@link Float} array. */ - public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0]; + public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = {}; /** * An empty immutable {@code int} array. */ - public static final int[] EMPTY_INT_ARRAY = new int[0]; + public static final int[] EMPTY_INT_ARRAY = {}; /** - * An empty immutable {@code Integer} array. + * An empty immutable {@link Integer} array. */ - public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0]; + public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = {}; /** * An empty immutable {@code long} array. */ - public static final long[] EMPTY_LONG_ARRAY = new long[0]; + public static final long[] EMPTY_LONG_ARRAY = {}; + + /** + * An empty immutable {@link Long} array. + */ + public static final Long[] EMPTY_LONG_OBJECT_ARRAY = {}; /** - * An empty immutable {@code Long} array. + * An empty immutable {@link Method} array. + * + * @since 3.10 */ - public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0]; + public static final Method[] EMPTY_METHOD_ARRAY = {}; /** - * An empty immutable {@code Object} array. + * An empty immutable {@link Object} array. */ - public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + public static final Object[] EMPTY_OBJECT_ARRAY = {}; /** * An empty immutable {@code short} array. */ - public static final short[] EMPTY_SHORT_ARRAY = new short[0]; + public static final short[] EMPTY_SHORT_ARRAY = {}; /** - * An empty immutable {@code Short} array. + * An empty immutable {@link Short} array. */ - public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0]; + public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = {}; /** - * An empty immutable {@code String} array. + * An empty immutable {@link String} array. */ - public static final String[] EMPTY_STRING_ARRAY = new String[0]; + public static final String[] EMPTY_STRING_ARRAY = {}; /** - * An empty immutable {@code Throwable} array. + * An empty immutable {@link Throwable} array. * * @since 3.10 */ - public static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0]; + public static final Throwable[] EMPTY_THROWABLE_ARRAY = {}; /** - * An empty immutable {@code Type} array. + * An empty immutable {@link Type} array. * * @since 3.10 */ - public static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; + public static final Type[] EMPTY_TYPE_ARRAY = {}; /** * The index value when an element is not found in a list or array: {@code -1}. @@ -179,24 +200,41 @@ public class ArrayUtils { public static final int INDEX_NOT_FOUND = -1; /** - *

Copies the given array and adds the given element at the end of the new array. + * The {@code SOFT_MAX_ARRAY_LENGTH} constant from Java's internal ArraySupport class. * - *

The new array contains the same elements of the input + * @since 3.19.0 + * @deprecated This variable will be final in 4.0; to guarantee immutability now, use {@link #SAFE_MAX_ARRAY_LENGTH}. + */ + @Deprecated + public static int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; + + /** + * The {@code MAX_ARRAY_LENGTH} constant from Java's internal ArraySupport class. + * + * @since 3.21.0 + */ + public static final int SAFE_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; + + /** + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, true)          = [true]
      * ArrayUtils.add([true], false)       = [true, false]
      * ArrayUtils.add([true, false], true) = [true, false, true]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static boolean[] add(final boolean[] array, final boolean element) { @@ -206,18 +244,19 @@ public static boolean[] add(final boolean[] array, final boolean element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0, true)          = [true]
      * ArrayUtils.add([true], 0, false)       = [false, true]
@@ -225,10 +264,10 @@ public static boolean[] add(final boolean[] array, final boolean element) {
      * ArrayUtils.add([true, false], 1, true) = [true, true, false]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, boolean[], boolean...)} and * may be removed in a future release. Please note the handling of {@code null} input arrays differs @@ -240,24 +279,25 @@ public static boolean[] add(final boolean[] array, final int index, final boolea } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0)   = [0]
      * ArrayUtils.add([1], 0)    = [1, 0]
      * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static byte[] add(final byte[] array, final byte element) { @@ -267,18 +307,19 @@ public static byte[] add(final byte[] array, final byte element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add([1], 0, 2)         = [2, 1]
      * ArrayUtils.add([2, 6], 2, 3)      = [2, 6, 3]
@@ -286,11 +327,11 @@ public static byte[] add(final byte[] array, final byte element) {
      * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element - * @throws IndexOutOfBoundsException if the index is out of range + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. + * @throws IndexOutOfBoundsException if the index is out of range. * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, byte[], byte...)} and * may be removed in a future release. Please note the handling of {@code null} input arrays differs @@ -302,24 +343,25 @@ public static byte[] add(final byte[] array, final int index, final byte element } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, '0')       = ['0']
      * ArrayUtils.add(['1'], '0')      = ['1', '0']
      * ArrayUtils.add(['1', '0'], '1') = ['1', '0', '1']
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static char[] add(final char[] array, final char element) { @@ -329,18 +371,19 @@ public static char[] add(final char[] array, final char element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0, 'a')            = ['a']
      * ArrayUtils.add(['a'], 0, 'b')           = ['b', 'a']
@@ -349,11 +392,11 @@ public static char[] add(final char[] array, final char element) {
      * ArrayUtils.add(['a', 'b', 'c'], 1, 't') = ['a', 't', 'b', 'c']
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element - * @throws IndexOutOfBoundsException if the index is out of range + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. + * @throws IndexOutOfBoundsException if the index is out of range. * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, char[], char...)} and * may be removed in a future release. Please note the handling of {@code null} input arrays differs @@ -365,24 +408,26 @@ public static char[] add(final char[] array, final int index, final char element } /** - *

Copies the given array and adds the given element at the end of the new array. + * Copies the given array and adds the given element at the end of the new array. * - *

The new array contains the same elements of the input + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0)   = [0]
      * ArrayUtils.add([1], 0)    = [1, 0]
      * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static double[] add(final double[] array, final double element) { @@ -392,18 +437,19 @@ public static double[] add(final double[] array, final double element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add([1.1], 0, 2.2)              = [2.2, 1.1]
      * ArrayUtils.add([2.3, 6.4], 2, 10.5)        = [2.3, 6.4, 10.5]
@@ -411,10 +457,10 @@ public static double[] add(final double[] array, final double element) {
      * ArrayUtils.add([2.9, 6.0, 0.3], 2, 1.0)    = [2.9, 6.0, 1.0, 0.3]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, double[], double...)} and @@ -427,24 +473,25 @@ public static double[] add(final double[] array, final int index, final double e } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0)   = [0]
      * ArrayUtils.add([1], 0)    = [1, 0]
      * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static float[] add(final float[] array, final float element) { @@ -454,18 +501,19 @@ public static float[] add(final float[] array, final float element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add([1.1f], 0, 2.2f)               = [2.2f, 1.1f]
      * ArrayUtils.add([2.3f, 6.4f], 2, 10.5f)        = [2.3f, 6.4f, 10.5f]
@@ -473,10 +521,10 @@ public static float[] add(final float[] array, final float element) {
      * ArrayUtils.add([2.9f, 6.0f, 0.3f], 2, 1.0f)   = [2.9f, 6.0f, 1.0f, 0.3f]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, float[], float...)} and @@ -489,24 +537,25 @@ public static float[] add(final float[] array, final int index, final float elem } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0)   = [0]
      * ArrayUtils.add([1], 0)    = [1, 0]
      * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static int[] add(final int[] array, final int element) { @@ -516,18 +565,19 @@ public static int[] add(final int[] array, final int element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add([1], 0, 2)         = [2, 1]
      * ArrayUtils.add([2, 6], 2, 10)     = [2, 6, 10]
@@ -535,10 +585,10 @@ public static int[] add(final int[] array, final int element) {
      * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, int[], int...)} and @@ -551,18 +601,19 @@ public static int[] add(final int[] array, final int index, final int element) { } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add([1L], 0, 2L)           = [2L, 1L]
      * ArrayUtils.add([2L, 6L], 2, 10L)      = [2L, 6L, 10L]
@@ -570,10 +621,10 @@ public static int[] add(final int[] array, final int index, final int element) {
      * ArrayUtils.add([2L, 6L, 3L], 2, 1L)   = [2L, 6L, 1L, 3L]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, long[], long...)} and @@ -586,24 +637,25 @@ public static long[] add(final long[] array, final int index, final long element } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0)   = [0]
      * ArrayUtils.add([1], 0)    = [1, 0]
      * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static long[] add(final long[] array, final long element) { @@ -617,18 +669,18 @@ public static long[] add(final long[] array, final long element) { * The last parameter is the class, which may not equal element.getClass * for primitives. * - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @param clss the type of the element being added - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @param clazz The type of the element being added. + * @return A new array containing the existing elements and the new element. */ - private static Object add(final Object array, final int index, final Object element, final Class clss) { + private static Object add(final Object array, final int index, final Object element, final Class clazz) { if (array == null) { if (index != 0) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: 0"); } - final Object joinedArray = Array.newInstance(clss, 1); + final Object joinedArray = Array.newInstance(clazz, 1); Array.set(joinedArray, 0, element); return joinedArray; } @@ -636,8 +688,7 @@ private static Object add(final Object array, final int index, final Object elem if (index > length || index < 0) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); } - final Object result = Array.newInstance(clss, length + 1); - System.arraycopy(array, 0, result, 0, index); + final Object result = arraycopy(array, 0, 0, index, () -> Array.newInstance(clazz, length + 1)); Array.set(result, index, element); if (index < length) { System.arraycopy(array, index, result, index + 1, length - index); @@ -646,18 +697,19 @@ private static Object add(final Object array, final int index, final Object elem } /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add([1], 0, 2)         = [2, 1]
      * ArrayUtils.add([2, 6], 2, 10)     = [2, 6, 10]
@@ -665,10 +717,10 @@ private static Object add(final Object array, final int index, final Object elem
      * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
      * 
* - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > array.length). * @deprecated this method has been superseded by {@link #insert(int, short[], short...)} and @@ -681,24 +733,25 @@ public static short[] add(final short[] array, final int index, final short elem } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
      * ArrayUtils.add(null, 0)   = [0]
      * ArrayUtils.add([1], 0)    = [1, 0]
      * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
      * 
* - * @param array the array to copy and add the element to, may be {@code null} - * @param element the object to add at the last index of the new array - * @return A new array containing the existing elements plus the new element + * @param array The array to copy and add the element to, may be {@code null}. + * @param element The object to add at the last index of the new array. + * @return A new array containing the existing elements plus the new element. * @since 2.1 */ public static short[] add(final short[] array, final short element) { @@ -707,85 +760,84 @@ public static short[] add(final short[] array, final short element) { return newArray; } - /** - *

Inserts the specified element at the specified position in the array. + * Inserts the specified element at the specified position in the array. * Shifts the element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array plus the given element on the specified position. The component * type of the returned array is always the same as that of the input * array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element. - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element. + *

*
-     * ArrayUtils.add(null, 0, null)      = IllegalArgumentException
+     * ArrayUtils.add(null, 0, null)      = Throws {@link IllegalArgumentException}
      * ArrayUtils.add(null, 0, "a")       = ["a"]
      * ArrayUtils.add(["a"], 1, null)     = ["a", null]
      * ArrayUtils.add(["a"], 1, "b")      = ["a", "b"]
      * ArrayUtils.add(["a", "b"], 3, "c") = ["a", "b", "c"]
      * 
* - * @param the component type of the array - * @param array the array to add the element to, may be {@code null} - * @param index the position of the new object - * @param element the object to add - * @return A new array containing the existing elements and the new element + * @param The component type of the array. + * @param array The array to add the element to, may be {@code null}. + * @param index The position of the new object. + * @param element The object to add. + * @return A new array containing the existing elements and the new element. * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index > array.length). - * @throws IllegalArgumentException if both array and element are null + * @throws IllegalArgumentException if both array and element are null. * @deprecated this method has been superseded by {@link #insert(int, Object[], Object...) insert(int, T[], T...)} and * may be removed in a future release. Please note the handling of {@code null} input arrays differs * in the new method: inserting {@code X} into a {@code null} array results in {@code null} not {@code X}. */ @Deprecated public static T[] add(final T[] array, final int index, final T element) { - Class clss = null; + final Class clazz; if (array != null) { - clss = array.getClass().getComponentType(); + clazz = getComponentType(array); } else if (element != null) { - clss = element.getClass(); + clazz = ObjectUtils.getClass(element); } else { throw new IllegalArgumentException("Array and element cannot both be null"); } - @SuppressWarnings("unchecked") // the add method creates an array of type clss, which is type T - final T[] newArray = (T[]) add(array, index, element, clss); - return newArray; + return (T[]) add(array, index, element, clazz); } /** - *

Copies the given array and adds the given element at the end of the new array. - * - *

The new array contains the same elements of the input + * Copies the given array and adds the given element at the end of the new array. + *

+ * The new array contains the same elements of the input * array plus the given element in the last position. The component type of * the new array is the same as that of the input array. - * - *

If the input array is {@code null}, a new one element array is returned - * whose component type is the same as the element, unless the element itself is null, - * in which case the return type is Object[] - * + *

+ *

+ * If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element, unless the element itself is null, + * in which case the return type is Object[] + *

*
-     * ArrayUtils.add(null, null)      = IllegalArgumentException
+     * ArrayUtils.add(null, null)      = Throws {@link IllegalArgumentException}
      * ArrayUtils.add(null, "a")       = ["a"]
      * ArrayUtils.add(["a"], null)     = ["a", null]
      * ArrayUtils.add(["a"], "b")      = ["a", "b"]
      * ArrayUtils.add(["a", "b"], "c") = ["a", "b", "c"]
      * 
* - * @param the component type of the array - * @param array the array to "add" the element to, may be {@code null} - * @param element the object to add, may be {@code null} + * @param The component type of the array. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add, may be {@code null}. * @return A new array containing the existing elements plus the new element * The returned array type will be that of the input array (unless null), * in which case it will have the same type as the element. - * If both are null, an IllegalArgumentException is thrown + * If both are null, an IllegalArgumentException is thrown. + * @throws IllegalArgumentException if both arguments are null. * @since 2.1 - * @throws IllegalArgumentException if both arguments are null */ public static T[] add(final T[] array, final T element) { - Class type; + final Class type; if (array != null) { type = array.getClass().getComponentType(); } else if (element != null) { @@ -801,284 +853,315 @@ public static T[] add(final T[] array, final T element) { } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new boolean[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new boolean[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static boolean[] addAll(final boolean[] array1, final boolean... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final boolean[] joinedArray = new boolean[array1.length + array2.length]; + final boolean[] joinedArray = new boolean[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new byte[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new byte[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static byte[] addAll(final byte[] array1, final byte... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final byte[] joinedArray = new byte[array1.length + array2.length]; + final byte[] joinedArray = new byte[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new char[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new char[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static char[] addAll(final char[] array1, final char... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final char[] joinedArray = new char[array1.length + array2.length]; + final char[] joinedArray = new char[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new double[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new double[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static double[] addAll(final double[] array1, final double... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final double[] joinedArray = new double[array1.length + array2.length]; + final double[] joinedArray = new double[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new float[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new float[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static float[] addAll(final float[] array1, final float... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final float[] joinedArray = new float[array1.length + array2.length]; + final float[] joinedArray = new float[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new int[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new int[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static int[] addAll(final int[] array1, final int... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final int[] joinedArray = new int[array1.length + array2.length]; + final int[] joinedArray = new int[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new long[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new long[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static long[] addAll(final long[] array1, final long... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final long[] joinedArray = new long[array1.length + array2.length]; + final long[] joinedArray = new long[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * 
* - * @param array1 the first array whose elements are added to the new array. - * @param array2 the second array whose elements are added to the new array. - * @return The new short[] array. + * @param array1 The first array whose elements are added to the new array. + * @param array2 The second array whose elements are added to the new array. + * @return The new short[] array or {@code null}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 */ public static short[] addAll(final short[] array1, final short... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final short[] joinedArray = new short[array1.length + array2.length]; + final short[] joinedArray = new short[addExact(array1.length, array2)]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } /** - *

Adds all the elements of the given arrays into a new array. - *

The new array contains all of the element of {@code array1} followed + * Adds all the elements of the given arrays into a new array. + *

+ * The new array contains all of the element of {@code array1} followed * by all of the elements {@code array2}. When an array is returned, it is always * a new array. - * + *

*
      * ArrayUtils.addAll(null, null)     = null
      * ArrayUtils.addAll(array1, null)   = cloned copy of array1
      * ArrayUtils.addAll(null, array2)   = cloned copy of array2
      * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll(null, null)     = null
      * ArrayUtils.addAll([null], [null]) = [null, null]
      * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
      * 
* - * @param the component type of the array - * @param array1 the first array whose elements are added to the new array, may be {@code null} - * @param array2 the second array whose elements are added to the new array, may be {@code null} + * @param The component type of the array. + * @param array1 The first array whose elements are added to the new array, may be {@code null}. + * @param array2 The second array whose elements are added to the new array, may be {@code null}. * @return The new array, {@code null} if both arrays are {@code null}. * The type of the new array is the type of the first array, * unless the first array is null, in which case the type is the same as the second array. + * @throws IllegalArgumentException if the array types are incompatible or if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 2.1 - * @throws IllegalArgumentException if the array types are incompatible */ public static T[] addAll(final T[] array1, @SuppressWarnings("unchecked") final T... array2) { if (array1 == null) { return clone(array2); - } else if (array2 == null) { + } + if (array2 == null) { return clone(array1); } - final Class type1 = array1.getClass().getComponentType(); - @SuppressWarnings("unchecked") // OK, because array is of type T - final T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length); - System.arraycopy(array1, 0, joinedArray, 0, array1.length); + final Class type1 = getComponentType(array1); + final T[] joinedArray = arraycopy(array1, 0, 0, array1.length, () -> newInstance(type1, addExact(array1.length, array2))); try { System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); } catch (final ArrayStoreException ase) { // Check if problem was due to incompatible types /* - * We do this here, rather than before the copy because: - * - it would be a wasted check most of the time - * - safer, in case check turns out to be too strict + * We do this here, rather than before the copy because: - it would be a wasted check most of the time - safer, in case check turns out to be too + * strict */ final Class type2 = array2.getClass().getComponentType(); if (!type1.isAssignableFrom(type2)) { - throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of " - + type1.getName(), ase); + throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of " + type1.getName(), ase); } throw ase; // No, so rethrow original } @@ -1086,26 +1169,44 @@ public static T[] addAll(final T[] array1, @SuppressWarnings("unchecked") fi } /** - * Copies the given array and adds the given element at the beginning of the new array. + * Safely adds the length of an array to a running total, checking for overflow. * + * @param totalLength The current accumulated length + * @param array The array whose length should be added (can be {@code null}, + * in which case its length is considered 0) + * @return The new total length after adding the array's length + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + */ + private static int addExact(final int totalLength, final Object array) { + try { + final int length = MathBridge.addExact(totalLength, getLength(array)); + if (length > SAFE_MAX_ARRAY_LENGTH) { + throw new IllegalArgumentException("Total arrays length exceed " + SAFE_MAX_ARRAY_LENGTH); + } + return length; + } catch (final ArithmeticException exception) { + throw new IllegalArgumentException("Total arrays length exceed " + SAFE_MAX_ARRAY_LENGTH); + } + } + + /** + * Copies the given array and adds the given element at the beginning of the new array. *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, true)          = [true]
-     * ArrayUtils.add([true], false)       = [false, true]
-     * ArrayUtils.add([true, false], true) = [true, true, false]
+     * ArrayUtils.addFirst(null, true)          = [true]
+     * ArrayUtils.addFirst([true], false)       = [false, true]
+     * ArrayUtils.addFirst([true, false], true) = [true, true, false]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1116,25 +1217,22 @@ public static boolean[] addFirst(final boolean[] array, final boolean element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, 1)   = [1]
-     * ArrayUtils.add([1], 0)    = [0, 1]
-     * ArrayUtils.add([1, 0], 1) = [1, 1, 0]
+     * ArrayUtils.addFirst(null, 1)   = [1]
+     * ArrayUtils.addFirst([1], 0)    = [0, 1]
+     * ArrayUtils.addFirst([1, 0], 1) = [1, 1, 0]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1145,25 +1243,22 @@ public static byte[] addFirst(final byte[] array, final byte element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, '1')       = ['1']
-     * ArrayUtils.add(['1'], '0')      = ['0', '1']
-     * ArrayUtils.add(['1', '0'], '1') = ['1', '1', '0']
+     * ArrayUtils.addFirst(null, '1')       = ['1']
+     * ArrayUtils.addFirst(['1'], '0')      = ['0', '1']
+     * ArrayUtils.addFirst(['1', '0'], '1') = ['1', '1', '0']
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1174,25 +1269,22 @@ public static char[] addFirst(final char[] array, final char element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, 1)   = [1]
-     * ArrayUtils.add([1], 0)    = [0, 1]
-     * ArrayUtils.add([1, 0], 1) = [1, 1, 0]
+     * ArrayUtils.addFirst(null, 1)   = [1]
+     * ArrayUtils.addFirst([1], 0)    = [0, 1]
+     * ArrayUtils.addFirst([1, 0], 1) = [1, 1, 0]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1203,25 +1295,22 @@ public static double[] addFirst(final double[] array, final double element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, 1)   = [1]
-     * ArrayUtils.add([1], 0)    = [0, 1]
-     * ArrayUtils.add([1, 0], 1) = [1, 1, 0]
+     * ArrayUtils.addFirst(null, 1)   = [1]
+     * ArrayUtils.addFirst([1], 0)    = [0, 1]
+     * ArrayUtils.addFirst([1, 0], 1) = [1, 1, 0]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1232,25 +1321,22 @@ public static float[] addFirst(final float[] array, final float element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, 1)   = [1]
-     * ArrayUtils.add([1], 0)    = [0, 1]
-     * ArrayUtils.add([1, 0], 1) = [1, 1, 0]
+     * ArrayUtils.addFirst(null, 1)   = [1]
+     * ArrayUtils.addFirst([1], 0)    = [0, 1]
+     * ArrayUtils.addFirst([1, 0], 1) = [1, 1, 0]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1261,25 +1347,22 @@ public static int[] addFirst(final int[] array, final int element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, 1)   = [1]
-     * ArrayUtils.add([1], 0)    = [0, 1]
-     * ArrayUtils.add([1, 0], 1) = [1, 1, 0]
+     * ArrayUtils.addFirst(null, 1)   = [1]
+     * ArrayUtils.addFirst([1], 0)    = [0, 1]
+     * ArrayUtils.addFirst([1, 0], 1) = [1, 1, 0]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1290,25 +1373,22 @@ public static long[] addFirst(final long[] array, final long element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

* The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element. *

- * *
-     * ArrayUtils.add(null, 1)   = [1]
-     * ArrayUtils.add([1], 0)    = [0, 1]
-     * ArrayUtils.add([1, 0], 1) = [1, 1, 0]
+     * ArrayUtils.addFirst(null, 1)   = [1]
+     * ArrayUtils.addFirst([1], 0)    = [0, 1]
+     * ArrayUtils.addFirst([1, 0], 1) = [1, 1, 0]
      * 
* - * @param array the array to "add" the element to, may be {@code null}. - * @param element the object to add. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. * @since 3.10 @@ -1319,222 +1399,518 @@ public static short[] addFirst(final short[] array, final short element) { /** * Copies the given array and adds the given element at the beginning of the new array. - * *

- * The new array contains the same elements of the input array plus the given element in the first positioaddFirstaddFirstaddFirstn. The + * The new array contains the same elements of the input array plus the given element in the first position. The * component type of the new array is the same as that of the input array. *

- * *

* If the input array is {@code null}, a new one element array is returned whose component type is the same as the * element, unless the element itself is null, in which case the return type is Object[] *

- * *
-     * ArrayUtils.add(null, null)      = IllegalArgumentException
-     * ArrayUtils.add(null, "a")       = ["a"]
-     * ArrayUtils.add(["a"], null)     = [null, "a"]
-     * ArrayUtils.add(["a"], "b")      = ["b", "a"]
-     * ArrayUtils.add(["a", "b"], "c") = ["c", "a", "b"]
+     * ArrayUtils.addFirst(null, null)      = Throws {@link IllegalArgumentException}
+     * ArrayUtils.addFirst(null, "a")       = ["a"]
+     * ArrayUtils.addFirst(["a"], null)     = [null, "a"]
+     * ArrayUtils.addFirst(["a"], "b")      = ["b", "a"]
+     * ArrayUtils.addFirst(["a", "b"], "c") = ["c", "a", "b"]
      * 
* - * @param the component type of the array - * @param array the array to "add" the element to, may be {@code null} - * @param element the object to add, may be {@code null} + * @param The component type of the array. + * @param array The array to "add" the element to, may be {@code null}. + * @param element The object to add, may be {@code null}. * @return A new array containing the existing elements plus the new element The returned array type will be that of * the input array (unless null), in which case it will have the same type as the element. If both are null, - * an IllegalArgumentException is thrown + * an IllegalArgumentException is thrown. + * @throws IllegalArgumentException if both arguments are null. * @since 3.10 - * @throws IllegalArgumentException if both arguments are null */ public static T[] addFirst(final T[] array, final T element) { return array == null ? add(array, element) : insert(0, array, element); } /** - *

Clones an array returning a typecast result and handling - * {@code null}. - * - *

This method returns {@code null} for a {@code null} input array. + * A fluent version of {@link System#arraycopy(Object, int, Object, int, int)} that returns the destination array. * - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param the type. + * @param source The source array. + * @param sourcePos starting position in the source array. + * @param destPos starting position in the destination data. + * @param length The number of array elements to be copied. + * @param allocator allocates the array to populate and return. + * @return dest + * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds. + * @throws ArrayStoreException if an element in the {@code src} array could not be stored into the {@code dest} array because of a type + * mismatch. + * @throws NullPointerException if either {@code src} or {@code dest} is {@code null}. + * @since 3.15.0 */ - public static boolean[] clone(final boolean[] array) { - if (array == null) { - return null; - } - return array.clone(); + public static T arraycopy(final T source, final int sourcePos, final int destPos, final int length, final Function allocator) { + return arraycopy(source, sourcePos, allocator.apply(length), destPos, length); } /** - *

Clones an array returning a typecast result and handling - * {@code null}. + * A fluent version of {@link System#arraycopy(Object, int, Object, int, int)} that returns the destination array. * - *

This method returns {@code null} for a {@code null} input array. - * - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param the type. + * @param source The source array. + * @param sourcePos starting position in the source array. + * @param destPos starting position in the destination data. + * @param length The number of array elements to be copied. + * @param allocator allocates the array to populate and return. + * @return dest + * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds. + * @throws ArrayStoreException if an element in the {@code src} array could not be stored into the {@code dest} array because of a type + * mismatch. + * @throws NullPointerException if either {@code src} or {@code dest} is {@code null}. + * @since 3.15.0 */ - public static byte[] clone(final byte[] array) { - if (array == null) { - return null; - } - return array.clone(); + public static T arraycopy(final T source, final int sourcePos, final int destPos, final int length, final Supplier allocator) { + return arraycopy(source, sourcePos, allocator.get(), destPos, length); } /** - *

Clones an array returning a typecast result and handling - * {@code null}. - * - *

This method returns {@code null} for a {@code null} input array. + * A fluent version of {@link System#arraycopy(Object, int, Object, int, int)} that returns the destination array. * - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param the type. + * @param source The source array. + * @param sourcePos starting position in the source array. + * @param dest The destination array. + * @param destPos starting position in the destination data. + * @param length The number of array elements to be copied. + * @return dest + * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds. + * @throws ArrayStoreException if an element in the {@code src} array could not be stored into the {@code dest} array because of a type + * mismatch. + * @throws NullPointerException if either {@code src} or {@code dest} is {@code null}. + * @since 3.15.0 */ - public static char[] clone(final char[] array) { - if (array == null) { - return null; - } - return array.clone(); + public static T arraycopy(final T source, final int sourcePos, final T dest, final int destPos, final int length) { + System.arraycopy(source, sourcePos, dest, destPos, length); + return dest; } /** - *

Clones an array returning a typecast result and handling - * {@code null}. + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - *

This method returns {@code null} for a {@code null} input array. + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. + */ + public static boolean[] clone(final boolean[] array) { + return array != null ? array.clone() : null; + } + + /** + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. */ - public static double[] clone(final double[] array) { - if (array == null) { - return null; - } - return array.clone(); + public static byte[] clone(final byte[] array) { + return array != null ? array.clone() : null; } /** - *

Clones an array returning a typecast result and handling - * {@code null}. + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - *

This method returns {@code null} for a {@code null} input array. + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. + */ + public static char[] clone(final char[] array) { + return array != null ? array.clone() : null; + } + + /** + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. */ - public static float[] clone(final float[] array) { - if (array == null) { - return null; - } - return array.clone(); + public static double[] clone(final double[] array) { + return array != null ? array.clone() : null; } /** - *

Clones an array returning a typecast result and handling - * {@code null}. + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - *

This method returns {@code null} for a {@code null} input array. + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. + */ + public static float[] clone(final float[] array) { + return array != null ? array.clone() : null; + } + + /** + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. */ public static int[] clone(final int[] array) { - if (array == null) { - return null; - } - return array.clone(); + return array != null ? array.clone() : null; } /** - *

Clones an array returning a typecast result and handling - * {@code null}. - * - *

This method returns {@code null} for a {@code null} input array. + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. */ public static long[] clone(final long[] array) { - if (array == null) { - return null; - } - return array.clone(); + return array != null ? array.clone() : null; } /** - *

Clones an array returning a typecast result and handling - * {@code null}. - * - *

This method returns {@code null} for a {@code null} input array. + * Clones an array or returns {@code null}. + *

+ * This method returns {@code null} for a {@code null} input array. + *

* - * @param array the array to clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input + * @param array The array to clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. */ public static short[] clone(final short[] array) { - if (array == null) { - return null; + return array != null ? array.clone() : null; + } + + /** + * Shallow clones an array or returns {@code null}. + *

+ * The objects in the array are not cloned, thus there is no special handling for multi-dimensional arrays. + *

+ *

+ * This method returns {@code null} for a {@code null} input array. + *

+ * + * @param the component type of the array. + * @param array The array to shallow clone, may be {@code null}. + * @return The cloned array, {@code null} if {@code null} input. + */ + public static T[] clone(final T[] array) { + return array != null ? array.clone() : null; + } + + /** + * Concatenates multiple boolean arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

+ * + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new boolean array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static boolean[] concat(final boolean[]... arrays) { + int totalLength = 0; + for (final boolean[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final boolean[] result = new boolean[totalLength]; + int currentPos = 0; + for (final boolean[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } } - return array.clone(); + return result; } - // Clone - //----------------------------------------------------------------------- /** - *

Shallow clones an array returning a typecast result and handling - * {@code null}. + * Concatenates multiple byte arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

* - *

The objects in the array are not cloned, thus there is no special - * handling for multi-dimensional arrays. + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new byte array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static byte[] concat(final byte[]... arrays) { + int totalLength = 0; + for (final byte[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final byte[] result = new byte[totalLength]; + int currentPos = 0; + for (final byte[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } + } + return result; + } + + /** + * Concatenates multiple char arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

* - *

This method returns {@code null} for a {@code null} input array. + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new char array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static char[] concat(final char[]... arrays) { + int totalLength = 0; + for (final char[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final char[] result = new char[totalLength]; + int currentPos = 0; + for (final char[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } + } + return result; + } + + /** + * Concatenates multiple double arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

* - * @param the component type of the array - * @param array the array to shallow clone, may be {@code null} - * @return the cloned array, {@code null} if {@code null} input - */ - public static T[] clone(final T[] array) { - if (array == null) { - return null; + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new double array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static double[] concat(final double[]... arrays) { + int totalLength = 0; + for (final double[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final double[] result = new double[totalLength]; + int currentPos = 0; + for (final double[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } } - return array.clone(); + return result; } /** - *

Checks if the value is in the given array. + * Concatenates multiple float arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

* - *

The method returns {@code false} if a {@code null} array is passed in. + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new float array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static float[] concat(final float[]... arrays) { + int totalLength = 0; + for (final float[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final float[] result = new float[totalLength]; + int currentPos = 0; + for (final float[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } + } + return result; + } + + /** + * Concatenates multiple int arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new int array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static int[] concat(final int[]... arrays) { + int totalLength = 0; + for (final int[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final int[] result = new int[totalLength]; + int currentPos = 0; + for (final int[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } + } + return result; + } + + /** + * Concatenates multiple long arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

+ * + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new long array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static long[] concat(final long[]... arrays) { + int totalLength = 0; + for (final long[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final long[] result = new long[totalLength]; + int currentPos = 0; + for (final long[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } + } + return result; + } + + /** + * Concatenates multiple short arrays into a single array. + *

+ * This method combines all input arrays in the order they are provided, + * creating a new array that contains all elements from the input arrays. + * The resulting array length is the sum of lengths of all non-null input arrays. + *

+ * + * @param arrays The arrays to concatenate. Can be empty, contain nulls, + * or be null itself (treated as empty varargs). + * @return A new short array containing all elements from the input arrays + * in the order they appear, or an empty array if no elements are present. + * @throws NullPointerException if the input array of arrays is null. + * @throws IllegalArgumentException if total arrays length exceed {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. + * @since 3.21.0 + */ + public static short[] concat(final short[]... arrays) { + int totalLength = 0; + for (final short[] array : arrays) { + totalLength = addExact(totalLength, array); + } + final short[] result = new short[totalLength]; + int currentPos = 0; + for (final short[] array : arrays) { + if (array != null && array.length > 0) { + System.arraycopy(array, 0, result, currentPos, array.length); + currentPos += array.length; + } + } + return result; + } + + /** + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ * + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final boolean[] array, final boolean valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(byte[])} and {@link Arrays#binarySearch(byte[], byte)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final byte[] array, final byte valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(char[])} and {@link Arrays#binarySearch(char[], char)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. * @since 2.1 */ public static boolean contains(final char[] array, final char valueToFind) { @@ -1542,100 +1918,173 @@ public static boolean contains(final char[] array, final char valueToFind) { } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(double[])} and {@link Arrays#binarySearch(double[], double)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final double[] array, final double valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** - *

Checks if a value falling within the given tolerance is in the + * Checks if a value falling within the given tolerance is in the * given array. If the array contains a value within the inclusive range * defined by (value - tolerance) to (value + tolerance). - * - *

The method returns {@code false} if a {@code null} array + *

+ * The method returns {@code false} if a {@code null} array * is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(double[])} and {@link Arrays#binarySearch(double[], double)}. + *

* - * @param array the array to search - * @param valueToFind the value to find - * @param tolerance the array contains the tolerance of the search - * @return true if value falling within tolerance is in array + * @param array The array to search. + * @param valueToFind The value to find. + * @param tolerance The array contains the tolerance of the search. + * @return true if value falling within tolerance is in array. */ public static boolean contains(final double[] array, final double valueToFind, final double tolerance) { return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND; } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(float[])} and {@link Arrays#binarySearch(float[], float)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final float[] array, final float valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(int[])} and {@link Arrays#binarySearch(int[], int)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final int[] array, final int valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(long[])} and {@link Arrays#binarySearch(long[], long)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final long[] array, final long valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** - *

Checks if the object is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the object is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(Object[], Comparator)} and {@link Arrays#binarySearch(Object[], Object)}. + *

* - * @param array the array to search through - * @param objectToFind the object to find - * @return {@code true} if the array contains the object + * @param array The array to search, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @return {@code true} if the array contains the object. */ public static boolean contains(final Object[] array, final Object objectToFind) { return indexOf(array, objectToFind) != INDEX_NOT_FOUND; } /** - *

Checks if the value is in the given array. - * - *

The method returns {@code false} if a {@code null} array is passed in. + * Checks if the value is in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(short[])} and {@link Arrays#binarySearch(short[], short)}. + *

* - * @param array the array to search through - * @param valueToFind the value to find - * @return {@code true} if the array contains the object + * @param array The array to search. + * @param valueToFind The value to find. + * @return {@code true} if the array contains the object. */ public static boolean contains(final short[] array, final short valueToFind) { return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } + /** + * Checks if any of the ints are in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(int[])} and {@link Arrays#binarySearch(int[], int)}. + *

+ * + * @param array The array to search. + * @param objectsToFind any of the ints to find. + * @return {@code true} if the array contains any of the ints. + * @since 3.18.0 + */ + public static boolean containsAny(final int[] array, final int... objectsToFind) { + return IntStreams.of(objectsToFind).anyMatch(e -> contains(array, e)); + } + + /** + * Checks if any of the objects are in the given array. + *

+ * The method returns {@code false} if a {@code null} array is passed in. + *

+ *

+ * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using + * {@link Arrays#sort(Object[], Comparator)} and {@link Arrays#binarySearch(Object[], Object)}. + *

+ * + * @param array The array to search, may be {@code null}. + * @param objectsToFind any of the objects to find, may be {@code null}. + * @return {@code true} if the array contains any of the objects. + * @since 3.13.0 + */ + public static boolean containsAny(final Object[] array, final Object... objectsToFind) { + return Streams.of(objectsToFind).anyMatch(e -> contains(array, e)); + } + /** * Returns a copy of the given array of size 1 greater than the argument. * The last value of the array is left to the default value. @@ -1655,13 +2104,74 @@ private static Object copyArrayGrow1(final Object array, final Class newArray return Array.newInstance(newArrayComponentType, 1); } - //----------------------------------------------------------------------- /** - *

Returns the length of the specified array. - * This method can deal with {@code Object} arrays and with primitive arrays. + * Gets the nTh element of an array or null if the index is out of bounds or the array is null. + * + * @param The type of array elements. + * @param array The array to index. + * @param index The index. + * @return The nTh element of an array or null if the index is out of bounds or the array is null. + * @since 3.11 + */ + public static T get(final T[] array, final int index) { + return get(array, index, null); + } + + /** + * Gets the nTh element of an array or a default value if the index is out of bounds. + * + * @param The type of array elements. + * @param array The array to index. + * @param index The index. + * @param defaultValue The return value of the given index is out of bounds. + * @return The nTh element of an array or a default value if the index is out of bounds. + * @since 3.11 + */ + public static T get(final T[] array, final int index, final T defaultValue) { + return isArrayIndexValid(array, index) ? array[index] : defaultValue; + } + + /** + * Gets an array's component type. * - *

If the input array is {@code null}, {@code 0} is returned. + * @param The array type. + * @param array The array. + * @return The component type. + * @since 3.13.0 + */ + public static Class getComponentType(final T[] array) { + return ClassUtils.getComponentType(ObjectUtils.getClass(array)); + } + + /** + * Gets the number of dimensions of an array. + *

+ * The JVM specification limits the number of dimensions to 255. + *

* + * @param array The array, may be {@code null}. + * @return The number of dimensions, 0 if the input is null or not an array. The JVM specification limits the number of dimensions to 255. + * @since 3.21.0 + * @see JVM specification Field Descriptors + */ + public static int getDimensions(final Object array) { + int dimensions = 0; + if (array != null) { + Class arrayClass = array.getClass(); + while (arrayClass.isArray()) { + dimensions++; + arrayClass = arrayClass.getComponentType(); + } + } + return dimensions; + } + + /** + * Gets the length of the specified array. + * This method handles {@link Object} arrays and primitive arrays. + *

+ * If the input array is {@code null}, {@code 0} is returned. + *

*
      * ArrayUtils.getLength(null)            = 0
      * ArrayUtils.getLength([])              = 0
@@ -1671,39 +2181,43 @@ private static Object copyArrayGrow1(final Object array, final Class newArray
      * ArrayUtils.getLength(["a", "b", "c"]) = 3
      * 
* - * @param array the array to retrieve the length from, may be null - * @return The length of the array, or {@code 0} if the array is {@code null} + * @param array The array to retrieve the length from, may be {@code null}. + * @return The length of the array, or {@code 0} if the array is {@code null}. * @throws IllegalArgumentException if the object argument is not an array. * @since 2.1 */ public static int getLength(final Object array) { - if (array == null) { - return 0; - } - return Array.getLength(array); + return array != null ? Array.getLength(array) : 0; } /** - *

Get a hash code for an array handling multi-dimensional arrays correctly. - * - *

Multi-dimensional primitive arrays are also handled correctly by this method. + * Gets a hash code for an array handling multidimensional arrays. + *

+ * Multi-dimensional primitive arrays are also handled by this method. + *

* - * @param array the array to get a hash code for, {@code null} returns zero - * @return a hash code for the array + * @param array The array to get a hash code for, may be {@code null}. + * @return A hash code for the array. + * @see HashCodeBuilder */ public static int hashCode(final Object array) { return new HashCodeBuilder().append(array).toHashCode(); } + static void increment(final Map occurrences, final K boxed) { + occurrences.computeIfAbsent(boxed, k -> new MutableInt()).increment(); + } + /** * Finds the indices of the given value in the array. + *

+ * This method returns an empty BitSet for a {@code null} input array. + *

* - *

This method returns an empty BitSet for a {@code null} input array.

- * - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final boolean[] array, final boolean valueToFind) { @@ -1712,50 +2226,44 @@ public static BitSet indexesOf(final boolean[] array, final boolean valueToFind) /** * Finds the indices of the given value in the array starting at the given index. + *

+ * This method returns an empty BitSet for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return an empty BitSet ({@code -1}). + *

* - *

This method returns an empty BitSet for a {@code null} input array.

- * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return an empty BitSet ({@code -1}).

- * - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} - * array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final boolean[] array, final boolean valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } /** * Finds the indices of the given value in the array. * - *

This method returns an empty BitSet for a {@code null} input array.

+ *

+ * This method returns an empty BitSet for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final byte[] array, final byte valueToFind) { @@ -1770,31 +2278,25 @@ public static BitSet indexesOf(final byte[] array, final byte valueToFind) { *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final byte[] array, final byte valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -1803,10 +2305,10 @@ public static BitSet indexesOf(final byte[] array, final byte valueToFind, int s * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final char[] array, final char valueToFind) { @@ -1821,31 +2323,25 @@ public static BitSet indexesOf(final char[] array, final char valueToFind) { *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final char[] array, final char valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -1854,10 +2350,10 @@ public static BitSet indexesOf(final char[] array, final char valueToFind, int s * *

This method returns empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final double[] array, final double valueToFind) { @@ -1874,11 +2370,11 @@ public static BitSet indexesOf(final double[] array, final double valueToFind) { * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param tolerance tolerance of the search - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param tolerance tolerance of the search. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final double[] array, final double valueToFind, final double tolerance) { @@ -1893,31 +2389,25 @@ public static BitSet indexesOf(final double[] array, final double valueToFind, f *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final double[] array, final double valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -1934,32 +2424,26 @@ public static BitSet indexesOf(final double[] array, final double valueToFind, i *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @param tolerance tolerance of the search - * @return a BitSet of the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @param tolerance tolerance of the search. + * @return A BitSet of the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final double[] array, final double valueToFind, int startIndex, final double tolerance) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex, tolerance); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex, tolerance); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -1968,10 +2452,10 @@ public static BitSet indexesOf(final double[] array, final double valueToFind, i * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final float[] array, final float valueToFind) { @@ -1986,31 +2470,25 @@ public static BitSet indexesOf(final float[] array, final float valueToFind) { *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final float[] array, final float valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -2019,10 +2497,10 @@ public static BitSet indexesOf(final float[] array, final float valueToFind, int * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final int[] array, final int valueToFind) { @@ -2037,31 +2515,25 @@ public static BitSet indexesOf(final int[] array, final int valueToFind) { *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final int[] array, final int valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -2070,10 +2542,10 @@ public static BitSet indexesOf(final int[] array, final int valueToFind, int sta * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final long[] array, final long valueToFind) { @@ -2088,31 +2560,25 @@ public static BitSet indexesOf(final long[] array, final long valueToFind) { *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final long[] array, final long valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -2121,10 +2587,10 @@ public static BitSet indexesOf(final long[] array, final long valueToFind, int s * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param objectToFind the object to find, may be {@code null} - * @return a BitSet of all the indices of the object within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @return A BitSet of all the indices of the object within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final Object[] array, final Object objectToFind) { @@ -2139,31 +2605,25 @@ public static BitSet indexesOf(final Object[] array, final Object objectToFind) *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param objectToFind the object to find, may be {@code null} - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the object within the array starting at the index, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the object within the array starting at the index, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final Object[] array, final Object objectToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, objectToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, objectToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } @@ -2172,10 +2632,10 @@ public static BitSet indexesOf(final Object[] array, final Object objectToFind, * *

This method returns an empty BitSet for a {@code null} input array.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final short[] array, final short valueToFind) { @@ -2190,73 +2650,61 @@ public static BitSet indexesOf(final short[] array, final short valueToFind) { *

A negative startIndex is treated as zero. A startIndex larger than the array * length will return an empty BitSet.

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return a BitSet of all the indices of the value within the array, - * an empty BitSet if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return A BitSet of all the indices of the value within the array, + * an empty BitSet if not found or {@code null} array input. * @since 3.10 */ public static BitSet indexesOf(final short[] array, final short valueToFind, int startIndex) { - BitSet bitSet = new BitSet(); - - if (array == null) { - return bitSet; - } - - while (startIndex < array.length) { - startIndex = indexOf(array, valueToFind, startIndex); - - if (startIndex == INDEX_NOT_FOUND) { - break; + final BitSet bitSet = new BitSet(); + if (array != null) { + while (startIndex < array.length) { + startIndex = indexOf(array, valueToFind, startIndex); + if (startIndex == INDEX_NOT_FOUND) { + break; + } + bitSet.set(startIndex); + ++startIndex; } - - bitSet.set(startIndex); - ++startIndex; } - return bitSet; } - // boolean IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final boolean[] array, final boolean valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} - * array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final boolean[] array, final boolean valueToFind, int startIndex) { + public static int indexOf(final boolean[] array, final boolean valueToFind, final int startIndex) { if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2264,44 +2712,39 @@ public static int indexOf(final boolean[] array, final boolean valueToFind, int return INDEX_NOT_FOUND; } - // byte IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final byte[] array, final byte valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final byte[] array, final byte valueToFind, int startIndex) { - if (array == null) { + public static int indexOf(final byte[] array, final byte valueToFind, final int startIndex) { + if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2309,17 +2752,15 @@ public static int indexOf(final byte[] array, final byte valueToFind, int startI return INDEX_NOT_FOUND; } - // char IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. * @since 2.1 */ public static int indexOf(final char[] array, final char valueToFind) { @@ -2327,28 +2768,25 @@ public static int indexOf(final char[] array, final char valueToFind) { } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. * @since 2.1 */ - public static int indexOf(final char[] array, final char valueToFind, int startIndex) { - if (array == null) { + public static int indexOf(final char[] array, final char valueToFind, final int startIndex) { + if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2356,61 +2794,58 @@ public static int indexOf(final char[] array, final char valueToFind, int startI return INDEX_NOT_FOUND; } - // double IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final double[] array, final double valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Finds the index of the given value within a given tolerance in the array. - * This method will return the index of the first value which falls between the region - * defined by valueToFind - tolerance and valueToFind + tolerance. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value within a given tolerance in the array. This method will return the index of the first value which falls between the + * region defined by valueToFind - tolerance and valueToFind + tolerance. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param tolerance tolerance of the search - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param tolerance tolerance of the search. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final double[] array, final double valueToFind, final double tolerance) { return indexOf(array, valueToFind, 0, tolerance); } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final double[] array, final double valueToFind, int startIndex) { + public static int indexOf(final double[] array, final double valueToFind, final int startIndex) { + if (Double.isNaN(valueToFind)) { + return indexOfNaN(array, startIndex); + } if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2419,32 +2854,31 @@ public static int indexOf(final double[] array, final double valueToFind, int st } /** - *

Finds the index of the given value in the array starting at the given index. - * This method will return the index of the first value which falls between the region - * defined by valueToFind - tolerance and valueToFind + tolerance. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. This method will return the index of the first value which falls between the + * region defined by valueToFind - tolerance and valueToFind + tolerance. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @param tolerance tolerance of the search - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @param tolerance tolerance of the search. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final double[] array, final double valueToFind, int startIndex, final double tolerance) { + public static int indexOf(final double[] array, final double valueToFind, final int startIndex, final double tolerance) { + if (Double.isNaN(valueToFind)) { + return indexOfNaN(array, startIndex); + } if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } final double min = valueToFind - tolerance; final double max = valueToFind + tolerance; - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (array[i] >= min && array[i] <= max) { return i; } @@ -2452,89 +2886,81 @@ public static int indexOf(final double[] array, final double valueToFind, int st return INDEX_NOT_FOUND; } - // float IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final float[] array, final float valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final float[] array, final float valueToFind, int startIndex) { + public static int indexOf(final float[] array, final float valueToFind, final int startIndex) { if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { - if (valueToFind == array[i]) { + final boolean searchNaN = Float.isNaN(valueToFind); + for (int i = max0(startIndex); i < array.length; i++) { + final float element = array[i]; + if (valueToFind == element || searchNaN && Float.isNaN(element)) { return i; } } return INDEX_NOT_FOUND; } - // int IndexOf -//----------------------------------------------------------------------- -/** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input - */ -public static int indexOf(final int[] array, final int valueToFind) { - return indexOf(array, valueToFind, 0); -} - /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. + */ + public static int indexOf(final int[] array, final int valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final int[] array, final int valueToFind, int startIndex) { - if (array == null) { + public static int indexOf(final int[] array, final int valueToFind, final int startIndex) { + if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2542,44 +2968,39 @@ public static int indexOf(final int[] array, final int valueToFind, int startInd return INDEX_NOT_FOUND; } - // long IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final long[] array, final long valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final long[] array, final long valueToFind, int startIndex) { - if (array == null) { + public static int indexOf(final long[] array, final long valueToFind, final int startIndex) { + if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2587,43 +3008,39 @@ public static int indexOf(final long[] array, final long valueToFind, int startI return INDEX_NOT_FOUND; } - // Object IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given object in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given object in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param objectToFind the object to find, may be {@code null} - * @return the index of the object within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @return The index of the object within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final Object[] array, final Object objectToFind) { return indexOf(array, objectToFind, 0); } /** - *

Finds the index of the given object in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given object in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param objectToFind the object to find, may be {@code null} - * @param startIndex the index to start searching at - * @return the index of the object within the array starting at the index, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @param startIndex The index to start searching. + * @return The index of the object within the array starting at the index, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final Object[] array, final Object objectToFind, int startIndex) { - if (array == null) { + if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } + startIndex = max0(startIndex); if (objectToFind == null) { for (int i = startIndex; i < array.length; i++) { if (array[i] == null) { @@ -2640,44 +3057,39 @@ public static int indexOf(final Object[] array, final Object objectToFind, int s return INDEX_NOT_FOUND; } - // short IndexOf - //----------------------------------------------------------------------- /** - *

Finds the index of the given value in the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the index of the given value in the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null} + * @param valueToFind The value to find. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int indexOf(final short[] array, final short valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Finds the index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + * Finds the index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@link #INDEX_NOT_FOUND} ({@code -1}). + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the index to start searching at - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The index to start searching. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ - public static int indexOf(final short[] array, final short valueToFind, int startIndex) { - if (array == null) { + public static int indexOf(final short[] array, final short valueToFind, final int startIndex) { + if (isEmpty(array)) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - startIndex = 0; - } - for (int i = startIndex; i < array.length; i++) { + for (int i = max0(startIndex); i < array.length; i++) { if (valueToFind == array[i]) { return i; } @@ -2686,9 +3098,29 @@ public static int indexOf(final short[] array, final short valueToFind, int star } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Finds the index of the NaN value in a double array. + * @param array The array to search for NaN, may be {@code null}. + * @param startIndex The index to start searching. + * @return The index of the NaN value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. + */ + private static int indexOfNaN(final double[] array, final int startIndex) { + if (isEmpty(array)) { + return INDEX_NOT_FOUND; + } + for (int i = max0(startIndex); i < array.length; i++) { + if (Double.isNaN(array[i])) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2696,27 +3128,25 @@ public static int indexOf(final short[] array, final short valueToFind, int star
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static boolean[] insert(final int index, final boolean[] array, final boolean... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final boolean[] result = new boolean[array.length + values.length]; - + final boolean[] result = new boolean[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2728,9 +3158,11 @@ public static boolean[] insert(final int index, final boolean[] array, final boo } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2738,27 +3170,25 @@ public static boolean[] insert(final int index, final boolean[] array, final boo
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static byte[] insert(final int index, final byte[] array, final byte... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final byte[] result = new byte[array.length + values.length]; - + final byte[] result = new byte[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2770,9 +3200,11 @@ public static byte[] insert(final int index, final byte[] array, final byte... v } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2780,27 +3212,25 @@ public static byte[] insert(final int index, final byte[] array, final byte... v
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static char[] insert(final int index, final char[] array, final char... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final char[] result = new char[array.length + values.length]; - + final char[] result = new char[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2812,9 +3242,11 @@ public static char[] insert(final int index, final char[] array, final char... v } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2822,27 +3254,25 @@ public static char[] insert(final int index, final char[] array, final char... v
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static double[] insert(final int index, final double[] array, final double... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final double[] result = new double[array.length + values.length]; - + final double[] result = new double[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2854,9 +3284,11 @@ public static double[] insert(final int index, final double[] array, final doubl } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2864,27 +3296,25 @@ public static double[] insert(final int index, final double[] array, final doubl
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static float[] insert(final int index, final float[] array, final float... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final float[] result = new float[array.length + values.length]; - + final float[] result = new float[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2896,9 +3326,11 @@ public static float[] insert(final int index, final float[] array, final float.. } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2906,27 +3338,25 @@ public static float[] insert(final int index, final float[] array, final float..
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static int[] insert(final int index, final int[] array, final int... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final int[] result = new int[array.length + values.length]; - + final int[] result = new int[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2938,9 +3368,11 @@ public static int[] insert(final int index, final int[] array, final int... valu } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2948,27 +3380,25 @@ public static int[] insert(final int index, final int[] array, final int... valu
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static long[] insert(final int index, final long[] array, final long... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final long[] result = new long[array.length + values.length]; - + final long[] result = new long[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -2980,9 +3410,11 @@ public static long[] insert(final int index, final long[] array, final long... v } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -2990,27 +3422,25 @@ public static long[] insert(final int index, final long[] array, final long... v
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ public static short[] insert(final int index, final short[] array, final short... values) { if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final short[] result = new short[array.length + values.length]; - + final short[] result = new short[addExact(array.length, values)]; System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -3022,9 +3452,11 @@ public static short[] insert(final int index, final short[] array, final short.. } /** - *

Inserts elements into an array at the given index (starting from zero).

+ * Inserts elements into an array at the given index (starting from zero). * - *

When an array is returned, it is always a new array.

+ *

+ * When an array is returned, it is always a new array. + *

* *
      * ArrayUtils.insert(index, null, null)      = null
@@ -3032,13 +3464,13 @@ public static short[] insert(final int index, final short[] array, final short..
      * ArrayUtils.insert(index, null, values)    = null
      * 
* - * @param The type of elements in {@code array} and {@code values} - * @param index the position within {@code array} to insert the new values - * @param array the array to insert the values into, may be {@code null} - * @param values the new values to insert, may be {@code null} - * @return The new array. - * @throws IndexOutOfBoundsException if {@code array} is provided - * and either {@code index < 0} or {@code index > array.length} + * @param The type of elements in {@code array} and {@code values}. + * @param index The position within {@code array} to insert the new values. + * @param array The array to insert the values into, may be {@code null}. + * @param values The new values to insert, may be {@code null}. + * @return The new array or {@code null} if the given array is {@code null}. + * @throws IndexOutOfBoundsException if {@code array} is provided and either {@code index < 0} or {@code index > array.length}. + * @throws IllegalArgumentException if the total array length exceeds {@link ArrayUtils#SAFE_MAX_ARRAY_LENGTH}. * @since 3.6 */ @SafeVarargs @@ -3050,22 +3482,18 @@ public static T[] insert(final int index, final T[] array, final T... values * array to the caller. We also avoid relying on the type of the vararg * array, by inspecting the component type of 'array'. */ - if (array == null) { return null; } - if (ArrayUtils.isEmpty(values)) { + if (isEmpty(values)) { return clone(array); } if (index < 0 || index > array.length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length); } - - final Class type = array.getClass().getComponentType(); - @SuppressWarnings("unchecked") // OK, because array and values are of type T - final - T[] result = (T[]) Array.newInstance(type, array.length + values.length); - + final Class type = getComponentType(array); + final int length = addExact(array.length, values); + final T[] result = newInstance(type, length); System.arraycopy(values, 0, result, index, values.length); if (index > 0) { System.arraycopy(array, 0, result, 0, index); @@ -3077,136 +3505,144 @@ public static T[] insert(final int index, final T[] array, final T... values } /** - * Returns whether a given array can safely be accessed at the given index. - * @param the component type of the array - * @param array the array to inspect, may be null - * @param index the index of the array to be inspected - * @return Whether the given index is safely-accessible in the given array - * @since 3.8 + * Checks if an array is empty or {@code null}. + * + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. */ - public static boolean isArrayIndexValid(T[] array, int index) { - if (getLength(array) == 0 || array.length <= index) { - return false; - } + private static boolean isArrayEmpty(final Object array) { + return getLength(array) == 0; + } - return index >= 0; + /** + * Tests whether a given array can safely be accessed at the given index. + * + *
+     * ArrayUtils.isArrayIndexValid(null, 0)       = false
+     * ArrayUtils.isArrayIndexValid([], 0)         = false
+     * ArrayUtils.isArrayIndexValid(["a"], 0)      = true
+     * 
+ * + * @param The component type of the array. + * @param array The array to inspect, may be {@code null}. + * @param index The index of the array to be inspected. + * @return Whether the given index is safely-accessible in the given array. + * @since 3.8 + */ + public static boolean isArrayIndexValid(final T[] array, final int index) { + return index >= 0 && getLength(array) > index; } /** - *

Checks if an array of primitive booleans is empty or {@code null}. + * Tests whether an array of primitive booleans is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final boolean[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } - // IndexOf search - // ---------------------------------------------------------------------- - /** - *

Checks if an array of primitive bytes is empty or {@code null}. + * Tests whether an array of primitive bytes is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final byte[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } /** - *

Checks if an array of primitive chars is empty or {@code null}. + * Tests whether an array of primitive chars is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final char[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } /** - *

Checks if an array of primitive doubles is empty or {@code null}. + * Tests whether an array of primitive doubles is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final double[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } /** - *

Checks if an array of primitive floats is empty or {@code null}. + * Tests whether an array of primitive floats is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final float[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } - - /** - *

Checks if an array of primitive ints is empty or {@code null}. + * Tests whether an array of primitive ints is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final int[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } /** - *

Checks if an array of primitive longs is empty or {@code null}. + * Tests whether an array of primitive longs is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final long[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } - // ---------------------------------------------------------------------- /** - *

Checks if an array of Objects is empty or {@code null}. + * Tests whether an array of Objects is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final Object[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } /** - *

Checks if an array of primitive shorts is empty or {@code null}. + * Tests whether an array of primitive shorts is empty or {@code null}. * - * @param array the array to test - * @return {@code true} if the array is empty or {@code null} + * @param array The array to test. + * @return {@code true} if the array is empty or {@code null}. * @since 2.1 */ public static boolean isEmpty(final short[] array) { - return getLength(array) == 0; + return isArrayEmpty(array); } - /** - *

Compares two arrays, using equals(), handling multi-dimensional arrays + /** + * Tests whether two arrays have equal content, using equals(), handling multidimensional arrays * correctly. + *

+ * Multi-dimensional primitive arrays are also handled correctly by this method. + *

* - *

Multi-dimensional primitive arrays are also handled correctly by this method. - * - * @param array1 the left hand array to compare, may be {@code null} - * @param array2 the right hand array to compare, may be {@code null} - * @return {@code true} if the arrays are equal - * @deprecated this method has been replaced by {@code java.util.Objects.deepEquals(Object, Object)} and will be + * @param array1 The left-hand side array to compare, may be {@code null}. + * @param array2 The right-hand side array to compare, may be {@code null}. + * @return {@code true} if the arrays are equal. + * @deprecated Replaced by {@code java.util.Objects.deepEquals(Object, Object)} and will be * removed from future releases. */ @Deprecated @@ -3215,10 +3651,10 @@ public static boolean isEquals(final Object array1, final Object array2) { } /** - *

Checks if an array of primitive booleans is not empty and not {@code null}. + * Tests whether an array of primitive booleans is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final boolean[] array) { @@ -3226,10 +3662,10 @@ public static boolean isNotEmpty(final boolean[] array) { } /** - *

Checks if an array of primitive bytes is not empty and not {@code null}. + * Tests whether an array of primitive bytes is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final byte[] array) { @@ -3237,10 +3673,10 @@ public static boolean isNotEmpty(final byte[] array) { } /** - *

Checks if an array of primitive chars is not empty and not {@code null}. + * Tests whether an array of primitive chars is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final char[] array) { @@ -3248,10 +3684,10 @@ public static boolean isNotEmpty(final char[] array) { } /** - *

Checks if an array of primitive doubles is not empty and not {@code null}. + * Tests whether an array of primitive doubles is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final double[] array) { @@ -3259,10 +3695,10 @@ public static boolean isNotEmpty(final double[] array) { } /** - *

Checks if an array of primitive floats is not empty and not {@code null}. + * Tests whether an array of primitive floats is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final float[] array) { @@ -3270,10 +3706,10 @@ public static boolean isNotEmpty(final float[] array) { } /** - *

Checks if an array of primitive ints is not empty and not {@code null}. + * Tests whether an array of primitive ints is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final int[] array) { @@ -3281,10 +3717,10 @@ public static boolean isNotEmpty(final int[] array) { } /** - *

Checks if an array of primitive longs is not empty and not {@code null}. + * Tests whether an array of primitive longs is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final long[] array) { @@ -3292,23 +3728,22 @@ public static boolean isNotEmpty(final long[] array) { } /** - *

Checks if an array of primitive shorts is not empty and not {@code null}. + * Tests whether an array of primitive shorts is not empty and not {@code null}. * - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final short[] array) { return !isEmpty(array); } - // ---------------------------------------------------------------------- /** - *

Checks if an array of Objects is not empty and not {@code null}. + * Tests whether an array of Objects is not empty and not {@code null}. * - * @param the component type of the array - * @param array the array to test - * @return {@code true} if the array is not empty and not {@code null} + * @param The component type of the array + * @param array The array to test. + * @return {@code true} if the array is not empty and not {@code null}. * @since 2.5 */ public static boolean isNotEmpty(final T[] array) { @@ -3316,134 +3751,129 @@ public static boolean isNotEmpty(final T[] array) { } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. - * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array - */ - public static boolean isSameLength(final boolean[] array1, final boolean[] array2) { + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. + * + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. + */ + public static boolean isSameLength(final boolean[] array1, final boolean[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final byte[] array1, final byte[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final char[] array1, final char[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final double[] array1, final double[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final float[] array1, final float[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final int[] array1, final int[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final long[] array1, final long[] array2) { return getLength(array1) == getLength(array2); } - // Is same length - //----------------------------------------------------------------------- /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. + *

+ * Any multi-dimensional aspects of the arrays are ignored. + *

* - *

Any multi-dimensional aspects of the arrays are ignored. + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. + * @since 3.11 + */ + public static boolean isSameLength(final Object array1, final Object array2) { + return getLength(array1) == getLength(array2); + } + + /** + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. + *

+ * Any multi-dimensional aspects of the arrays are ignored. + *

* - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final Object[] array1, final Object[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same length, treating - * {@code null} arrays as length {@code 0}. + * Tests whether two arrays are the same length, treating {@code null} arrays as length {@code 0}. * - * @param array1 the first array, may be {@code null} - * @param array2 the second array, may be {@code null} - * @return {@code true} if length of arrays matches, treating - * {@code null} as an empty array + * @param array1 The first array, may be {@code null}. + * @param array2 The second array, may be {@code null}. + * @return {@code true} if length of arrays matches, treating {@code null} as an empty array. */ public static boolean isSameLength(final short[] array1, final short[] array2) { return getLength(array1) == getLength(array2); } /** - *

Checks whether two arrays are the same type taking into account - * multi-dimensional arrays. + * Tests whether two arrays are the same type taking into account multidimensional arrays. * - * @param array1 the first array, must not be {@code null} - * @param array2 the second array, must not be {@code null} - * @return {@code true} if type of arrays matches - * @throws IllegalArgumentException if either array is {@code null} + * @param array1 The first array, must not be {@code null}. + * @param array2 The second array, must not be {@code null}. + * @return {@code true} if type of arrays matches. + * @throws IllegalArgumentException if either array is {@code null}. */ public static boolean isSameType(final Object array1, final Object array2) { if (array1 == null || array2 == null) { @@ -3453,18 +3883,16 @@ public static boolean isSameType(final Object array1, final Object array2) { } /** - *

This method checks whether the provided array is sorted according to natural ordering - * ({@code false} before {@code true}). + * Tests whether the provided array is sorted according to natural ordering ({@code false} before {@code true}). * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final boolean[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - boolean previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { @@ -3472,49 +3900,45 @@ public static boolean isSorted(final boolean[] array) { if (BooleanUtils.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final byte[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - byte previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { final byte current = array[i]; - if (NumberUtils.compare(previous, current) > 0) { + if (Byte.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final char[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - char previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { @@ -3522,24 +3946,22 @@ public static boolean isSorted(final char[] array) { if (CharUtils.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final double[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - double previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { @@ -3547,24 +3969,22 @@ public static boolean isSorted(final double[] array) { if (Double.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final float[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - float previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { @@ -3572,118 +3992,108 @@ public static boolean isSorted(final float[] array) { if (Float.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final int[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - int previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { final int current = array[i]; - if (NumberUtils.compare(previous, current) > 0) { + if (Integer.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final long[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - long previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { final long current = array[i]; - if (NumberUtils.compare(previous, current) > 0) { + if (Long.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to natural ordering. + * Tests whether the provided array is sorted according to natural ordering. * - * @param array the array to check - * @return whether the array is sorted according to natural ordering + * @param array The array to check. + * @return whether the array is sorted according to natural ordering. * @since 3.4 */ public static boolean isSorted(final short[] array) { - if (array == null || array.length < 2) { + if (getLength(array) < 2) { return true; } - short previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { final short current = array[i]; - if (NumberUtils.compare(previous, current) > 0) { + if (Short.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

This method checks whether the provided array is sorted according to the class's + * Tests whether the provided array is sorted according to the class's * {@code compareTo} method. * - * @param array the array to check - * @param the datatype of the array to check, it must implement {@code Comparable} - * @return whether the array is sorted + * @param array The array to check. + * @param The datatype of the array to check, it must implement {@link Comparable}. + * @return whether the array is sorted. * @since 3.4 */ public static > boolean isSorted(final T[] array) { - return isSorted(array, (o1, o2) -> o1.compareTo(o2)); + return isSorted(array, Comparable::compareTo); } /** - *

This method checks whether the provided array is sorted according to the provided {@code Comparator}. + * Tests whether the provided array is sorted according to the provided {@link Comparator}. * - * @param array the array to check - * @param comparator the {@code Comparator} to compare over - * @param the datatype of the array - * @return whether the array is sorted + * @param array The array to check. + * @param comparator The {@link Comparator} to compare over. + * @param The datatype of the array. + * @return whether the array is sorted. + * @throws NullPointerException if {@code comparator} is {@code null}. * @since 3.4 */ public static boolean isSorted(final T[] array, final Comparator comparator) { - if (comparator == null) { - throw new IllegalArgumentException("Comparator should not be null."); - } - - if (array == null || array.length < 2) { + Objects.requireNonNull(comparator, "comparator"); + if (getLength(array) < 2) { return true; } - T previous = array[0]; final int n = array.length; for (int i = 1; i < n; i++) { @@ -3691,48 +4101,44 @@ public static boolean isSorted(final T[] array, final Comparator comparat if (comparator.compare(previous, current) > 0) { return false; } - previous = current; } return true; } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) if - * {@code null} array input. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) if {@code null} array input. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final boolean[] array, final boolean valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than - * the array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final boolean[] array, final boolean valueToFind, int startIndex) { - if (isEmpty(array)) { + if (isEmpty(array) || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -3744,40 +4150,38 @@ public static int lastIndexOf(final boolean[] array, final boolean valueToFind, } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final byte[] array, final byte valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final byte[] array, final byte valueToFind, int startIndex) { - if (array == null) { + if (array == null || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -3789,14 +4193,14 @@ public static int lastIndexOf(final byte[] array, final byte valueToFind, int st } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. * @since 2.1 */ public static int lastIndexOf(final char[] array, final char valueToFind) { @@ -3804,27 +4208,25 @@ public static int lastIndexOf(final char[] array, final char valueToFind) { } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. * @since 2.1 */ public static int lastIndexOf(final char[] array, final char valueToFind, int startIndex) { - if (array == null) { + if (array == null || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -3836,57 +4238,57 @@ public static int lastIndexOf(final char[] array, final char valueToFind, int st } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final double[] array, final double valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value within a given tolerance in the array. - * This method will return the index of the last value which falls between the region - * defined by valueToFind - tolerance and valueToFind + tolerance. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within a given tolerance in the array. This method will return the index of the last value which falls between + * the region defined by valueToFind - tolerance and valueToFind + tolerance. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to search through for the object, may be {@code null} - * @param valueToFind the value to find - * @param tolerance tolerance of the search - * @return the index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to search for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param tolerance tolerance of the search. + * @return The index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final double[] array, final double valueToFind, final double tolerance) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final double[] array, final double valueToFind, int startIndex) { - if (isEmpty(array)) { - return INDEX_NOT_FOUND; + if (Double.isNaN(valueToFind)) { + return lastIndexOfNaN(array, startIndex); } - if (startIndex < 0) { + if (isEmpty(array) || startIndex < 0) { return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + } + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -3898,29 +4300,29 @@ public static int lastIndexOf(final double[] array, final double valueToFind, in } /** - *

Finds the last index of the given value in the array starting at the given index. - * This method will return the index of the last value which falls between the region - * defined by valueToFind - tolerance and valueToFind + tolerance. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. This method will return the index of the last value which falls between + * the region defined by valueToFind - tolerance and valueToFind + tolerance. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @param tolerance search for value within plus/minus this amount - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @param tolerance search for value within plus/minus this amount. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final double[] array, final double valueToFind, int startIndex, final double tolerance) { - if (isEmpty(array)) { - return INDEX_NOT_FOUND; + if (Double.isNaN(valueToFind)) { + return lastIndexOfNaN(array, startIndex); } - if (startIndex < 0) { + if (isEmpty(array) || startIndex < 0) { return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + } + if (startIndex >= array.length) { startIndex = array.length - 1; } final double min = valueToFind - tolerance; @@ -3933,46 +4335,45 @@ public static int lastIndexOf(final double[] array, final double valueToFind, in return INDEX_NOT_FOUND; } - /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final float[] array, final float valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final float[] array, final float valueToFind, int startIndex) { - if (isEmpty(array)) { + if (isEmpty(array) || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } + final boolean searchNaN = Float.isNaN(valueToFind); for (int i = startIndex; i >= 0; i--) { - if (valueToFind == array[i]) { + final float element = array[i]; + if (valueToFind == element || searchNaN && Float.isNaN(element)) { return i; } } @@ -3980,40 +4381,38 @@ public static int lastIndexOf(final float[] array, final float valueToFind, int } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final int[] array, final int valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final int[] array, final int valueToFind, int startIndex) { - if (array == null) { + if (array == null || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -4025,40 +4424,38 @@ public static int lastIndexOf(final int[] array, final int valueToFind, int star } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final long[] array, final long valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final long[] array, final long valueToFind, int startIndex) { - if (array == null) { + if (array == null || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -4070,40 +4467,38 @@ public static int lastIndexOf(final long[] array, final long valueToFind, int st } /** - *

Finds the last index of the given object within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given object within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param objectToFind the object to find, may be {@code null} - * @return the last index of the object within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @return The last index of the object within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final Object[] array, final Object objectToFind) { return lastIndexOf(array, objectToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given object in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than - * the array length will search from the end of the array. + * Finds the last index of the given object in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param objectToFind the object to find, may be {@code null} - * @param startIndex the start index to traverse backwards from - * @return the last index of the object within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param objectToFind The object to find, may be {@code null}. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the object within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final Object[] array, final Object objectToFind, int startIndex) { - if (array == null) { + if (array == null || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } if (objectToFind == null) { @@ -4123,40 +4518,38 @@ public static int lastIndexOf(final Object[] array, final Object objectToFind, i } /** - *

Finds the last index of the given value within the array. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + * Finds the last index of the given value within the array. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

* - * @param array the array to traverse backwards looking for the object, may be {@code null} - * @param valueToFind the object to find - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse backwards looking for the object, may be {@code null}. + * @param valueToFind The object to find. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final short[] array, final short valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Finds the last index of the given value in the array starting at the given index. - * - *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. - * - *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the - * array length will search from the end of the array. + * Finds the last index of the given value in the array starting at the given index. + *

+ * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array. + *

+ *

+ * A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the array length will search from the end of the array. + *

* - * @param array the array to traverse for looking for the object, may be {@code null} - * @param valueToFind the value to find - * @param startIndex the start index to traverse backwards from - * @return the last index of the value within the array, - * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @param array The array to traverse for looking for the object, may be {@code null}. + * @param valueToFind The value to find. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. */ public static int lastIndexOf(final short[] array, final short valueToFind, int startIndex) { - if (array == null) { + if (array == null || startIndex < 0) { return INDEX_NOT_FOUND; } - if (startIndex < 0) { - return INDEX_NOT_FOUND; - } else if (startIndex >= array.length) { + if (startIndex >= array.length) { startIndex = array.length - 1; } for (int i = startIndex; i >= 0; i--) { @@ -4168,408 +4561,459 @@ public static int lastIndexOf(final short[] array, final short valueToFind, int } /** - *

Defensive programming technique to change a {@code null} - * reference to an empty one. + * Finds the last index of the NaN value in a double array. + * @param array The array to traverse backwards for NaN, may be {@code null}. + * @param startIndex The start index to traverse backwards from. + * @return The last index of the NaN value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input. + */ + private static int lastIndexOfNaN(final double[] array, final int startIndex) { + if (isEmpty(array) || startIndex < 0) { + return INDEX_NOT_FOUND; + } + for (int i = Math.min(startIndex, array.length - 1); i >= 0; i--) { + if (Double.isNaN(array[i])) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * Maps elements from an array into elements of a new array of a given type, while mapping old elements to new elements. + * + * @param The input array type. + * @param The output array type. + * @param The type of exceptions thrown when the mapper function fails. + * @param array The input array. + * @param componentType The component type of the result array. + * @param mapper A non-interfering, stateless function to apply to each element. + * @return A new array. + * @throws E Thrown when the mapper function fails. + */ + private static R[] map(final T[] array, final Class componentType, final FailableFunction mapper) + throws E { + return ArrayFill.fill(newInstance(componentType, array.length), i -> mapper.apply(array[i])); + } + + private static int max0(final int other) { + return Math.max(0, other); + } + + /** + * Delegates to {@link Array#newInstance(Class,int)} using generics. * - *

This method returns an empty array for a {@code null} input array. + * @param The array type. + * @param componentType The array class. + * @param length The array length + * @return The new array. + * @throws NullPointerException if the specified {@code componentType} parameter is null. + * @since 3.13.0 + */ + @SuppressWarnings("unchecked") // OK, because array and values are of type T + public static T[] newInstance(final Class componentType, final int length) { + return (T[]) Array.newInstance(componentType, length); + } + + /** + * Defensive programming technique to change a {@code null} + * reference to an empty one. + *

+ * This method returns a default array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class. + *

* - *

As a memory optimizing technique an empty array passed in will be overridden with + * @param The array type. + * @param array The array to check for {@code null} or empty + * @param defaultArray A default array, usually empty. + * @return The same array, or defaultArray if {@code null} or empty input. + * @since 3.15.0 + */ + public static T[] nullTo(final T[] array, final T[] defaultArray) { + return isEmpty(array) ? defaultArray : array; + } + + /** + * Defensive programming technique to change a {@code null} + * reference to an empty one. + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static boolean[] nullToEmpty(final boolean[] array) { - if (isEmpty(array)) { - return EMPTY_BOOLEAN_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_BOOLEAN_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Boolean[] nullToEmpty(final Boolean[] array) { - if (isEmpty(array)) { - return EMPTY_BOOLEAN_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_BOOLEAN_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static byte[] nullToEmpty(final byte[] array) { - if (isEmpty(array)) { - return EMPTY_BYTE_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_BYTE_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Byte[] nullToEmpty(final Byte[] array) { - if (isEmpty(array)) { - return EMPTY_BYTE_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_BYTE_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static char[] nullToEmpty(final char[] array) { - if (isEmpty(array)) { - return EMPTY_CHAR_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_CHAR_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Character[] nullToEmpty(final Character[] array) { - if (isEmpty(array)) { - return EMPTY_CHARACTER_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_CHARACTER_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 3.2 */ public static Class[] nullToEmpty(final Class[] array) { - if (isEmpty(array)) { - return EMPTY_CLASS_ARRAY; - } - return array; + return nullTo(array, EMPTY_CLASS_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static double[] nullToEmpty(final double[] array) { - if (isEmpty(array)) { - return EMPTY_DOUBLE_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_DOUBLE_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Double[] nullToEmpty(final Double[] array) { - if (isEmpty(array)) { - return EMPTY_DOUBLE_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_DOUBLE_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static float[] nullToEmpty(final float[] array) { - if (isEmpty(array)) { - return EMPTY_FLOAT_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_FLOAT_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Float[] nullToEmpty(final Float[] array) { - if (isEmpty(array)) { - return EMPTY_FLOAT_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_FLOAT_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static int[] nullToEmpty(final int[] array) { - if (isEmpty(array)) { - return EMPTY_INT_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_INT_ARRAY : array; } - // Primitive/Object array converters - // ---------------------------------------------------------------------- - /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Integer[] nullToEmpty(final Integer[] array) { - if (isEmpty(array)) { - return EMPTY_INTEGER_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_INTEGER_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static long[] nullToEmpty(final long[] array) { - if (isEmpty(array)) { - return EMPTY_LONG_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_LONG_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Long[] nullToEmpty(final Long[] array) { - if (isEmpty(array)) { - return EMPTY_LONG_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_LONG_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Object[] nullToEmpty(final Object[] array) { - if (isEmpty(array)) { - return EMPTY_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static short[] nullToEmpty(final short[] array) { - if (isEmpty(array)) { - return EMPTY_SHORT_ARRAY; - } - return array; + return isEmpty(array) ? EMPTY_SHORT_ARRAY : array; } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static Short[] nullToEmpty(final Short[] array) { - if (isEmpty(array)) { - return EMPTY_SHORT_OBJECT_ARRAY; - } - return array; + return nullTo(array, EMPTY_SHORT_OBJECT_ARRAY); } /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. - * - *

This method returns an empty array for a {@code null} input array. - * - *

As a memory optimizing technique an empty array passed in will be overridden with + *

+ * This method returns an empty array for a {@code null} input array. + *

+ *

+ * As a memory optimizing technique an empty array passed in will be overridden with * the empty {@code public static} references in this class. + *

* - * @param array the array to check for {@code null} or empty - * @return the same array, {@code public static} empty array if {@code null} or empty input + * @param array The array to check for {@code null} or empty. + * @return The same array, {@code public static} empty array if {@code null} or empty input. * @since 2.5 */ public static String[] nullToEmpty(final String[] array) { - if (isEmpty(array)) { - return EMPTY_STRING_ARRAY; - } - return array; + return nullTo(array, EMPTY_STRING_ARRAY); } - // nullToEmpty - //----------------------------------------------------------------------- /** - *

Defensive programming technique to change a {@code null} + * Defensive programming technique to change a {@code null} * reference to an empty one. + *

+ * This method returns an empty array for a {@code null} input array. + *

* - *

This method returns an empty array for a {@code null} input array. - * - * @param array the array to check for {@code null} or empty - * @param type the class representation of the desired array - * @param the class type - * @return the same array, {@code public static} empty array if {@code null} - * @throws IllegalArgumentException if the type argument is null + * @param array The array to check for {@code null} or empty. + * @param type The class representation of the desired array. + * @param the class type. + * @return The same array, {@code public static} empty array if {@code null}. + * @throws IllegalArgumentException if the type argument is null. * @since 3.5 */ public static T[] nullToEmpty(final T[] array, final Class type) { if (type == null) { throw new IllegalArgumentException("The type must not be null"); } - if (array == null) { return type.cast(Array.newInstance(type.getComponentType(), 0)); } @@ -4577,17 +5021,24 @@ public static T[] nullToEmpty(final T[] array, final Class type) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. + * Gets the current thread's {@link ThreadLocalRandom} for {@code shuffle} methods that don't take a {@link Random} argument. * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * @return The current ThreadLocalRandom. + */ + private static ThreadLocalRandom random() { + return ThreadLocalRandom.current(); + } + + /** + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([true], 0)              = []
@@ -4596,12 +5047,10 @@ public static  T[] nullToEmpty(final T[] array, final Class type) {
      * ArrayUtils.remove([true, true, false], 1) = [true, false]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static boolean[] remove(final boolean[] array, final int index) { @@ -4609,17 +5058,15 @@ public static boolean[] remove(final boolean[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([1], 0)          = []
@@ -4628,12 +5075,10 @@ public static boolean[] remove(final boolean[] array, final int index) {
      * ArrayUtils.remove([1, 0, 1], 1)    = [1, 1]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static byte[] remove(final byte[] array, final int index) { @@ -4641,17 +5086,15 @@ public static byte[] remove(final byte[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove(['a'], 0)           = []
@@ -4660,12 +5103,10 @@ public static byte[] remove(final byte[] array, final int index) {
      * ArrayUtils.remove(['a', 'b', 'c'], 1) = ['a', 'c']
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static char[] remove(final char[] array, final int index) { @@ -4673,17 +5114,15 @@ public static char[] remove(final char[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([1.1], 0)           = []
@@ -4692,12 +5131,10 @@ public static char[] remove(final char[] array, final int index) {
      * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static double[] remove(final double[] array, final int index) { @@ -4705,17 +5142,15 @@ public static double[] remove(final double[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([1.1], 0)           = []
@@ -4724,12 +5159,10 @@ public static double[] remove(final double[] array, final int index) {
      * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static float[] remove(final float[] array, final int index) { @@ -4737,17 +5170,15 @@ public static float[] remove(final float[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([1], 0)         = []
@@ -4756,12 +5187,10 @@ public static float[] remove(final float[] array, final int index) {
      * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static int[] remove(final int[] array, final int index) { @@ -4769,17 +5198,15 @@ public static int[] remove(final int[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([1], 0)         = []
@@ -4788,12 +5215,10 @@ public static int[] remove(final int[] array, final int index) {
      * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static long[] remove(final long[] array, final int index) { @@ -4801,24 +5226,20 @@ public static long[] remove(final long[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ private static Object remove(final Object array, final int index) { @@ -4826,28 +5247,24 @@ private static Object remove(final Object array, final int index) { if (index < 0 || index >= length) { throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); } - final Object result = Array.newInstance(array.getClass().getComponentType(), length - 1); System.arraycopy(array, 0, result, 0, index); if (index < length - 1) { System.arraycopy(array, index + 1, result, index, length - index - 1); } - return result; } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove([1], 0)         = []
@@ -4856,12 +5273,10 @@ private static Object remove(final Object array, final int index) {
      * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ public static short[] remove(final short[] array, final int index) { @@ -4869,17 +5284,15 @@ public static short[] remove(final short[] array, final int index) { } /** - *

Removes the element at the specified position from the specified array. - * All subsequent elements are shifted to the left (subtracts one from - * their indices). - * - *

This method returns a new array with the same elements of the input - * array except the element on the specified position. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the element at the specified position from the specified array. All subsequent elements are shifted to the left (subtracts one from their + * indices). + *

+ * This method returns a new array with the same elements of the input array except the element on the specified position. The component type of the + * returned array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, an IndexOutOfBoundsException will be thrown, because in that case no valid index can be specified. + *

* *
      * ArrayUtils.remove(["a"], 0)           = []
@@ -4888,13 +5301,11 @@ public static short[] remove(final short[] array, final int index) {
      * ArrayUtils.remove(["a", "b", "c"], 1) = ["a", "c"]
      * 
* - * @param the component type of the array - * @param array the array to remove the element from, may not be {@code null} - * @param index the position of the element to be removed - * @return A new array containing the existing elements except the element - * at the specified position. - * @throws IndexOutOfBoundsException if the index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param the component type of the array. + * @param array The array to remove the element from, may not be {@code null}. + * @param index The position of the element to be removed. + * @return A new array containing the existing elements except the element at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= array.length), or if the array is {@code null}. * @since 2.1 */ @SuppressWarnings("unchecked") // remove() always creates an array of the same type as its input @@ -4903,28 +5314,24 @@ public static T[] remove(final T[] array, final int index) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([true, false, true], 0, 2) = [false]
      * ArrayUtils.removeAll([true, false, true], 1, 2) = [true]
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static boolean[] removeAll(final boolean[] array, final int... indices) { @@ -4932,16 +5339,14 @@ public static boolean[] removeAll(final boolean[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -4952,12 +5357,10 @@ public static boolean[] removeAll(final boolean[] array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static byte[] removeAll(final byte[] array, final int... indices) { @@ -4965,16 +5368,14 @@ public static byte[] removeAll(final byte[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -4985,12 +5386,10 @@ public static byte[] removeAll(final byte[] array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static char[] removeAll(final char[] array, final int... indices) { @@ -4998,16 +5397,14 @@ public static char[] removeAll(final char[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -5018,12 +5415,10 @@ public static char[] removeAll(final char[] array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static double[] removeAll(final double[] array, final int... indices) { @@ -5031,16 +5426,14 @@ public static double[] removeAll(final double[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -5051,12 +5444,10 @@ public static double[] removeAll(final double[] array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static float[] removeAll(final float[] array, final int... indices) { @@ -5064,16 +5455,14 @@ public static float[] removeAll(final float[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -5084,12 +5473,10 @@ public static float[] removeAll(final float[] array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static int[] removeAll(final int[] array, final int... indices) { @@ -5097,16 +5484,14 @@ public static int[] removeAll(final int[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -5117,12 +5502,10 @@ public static int[] removeAll(final int[] array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static long[] removeAll(final long[] array, final int... indices) { @@ -5130,61 +5513,20 @@ public static long[] removeAll(final long[] array, final int... indices) { } /** - * Removes multiple array elements specified by indices. + * Removes multiple array elements specified by index. * - * @param array source + * @param array source * @param indices to remove - * @return new array of same type minus elements specified by the set bits in {@code indices} - * @since 3.2 + * @return new array of same type minus elements specified by unique values of {@code indices} */ // package protected for access by unit tests - static Object removeAll(final Object array, final BitSet indices) { + static Object removeAll(final Object array, final int... indices) { if (array == null) { return null; } - - final int srcLength = getLength(array); - // No need to check maxIndex here, because method only currently called from removeElements() - // which guarantee to generate on;y valid bit entries. -// final int maxIndex = indices.length(); -// if (maxIndex > srcLength) { -// throw new IndexOutOfBoundsException("Index: " + (maxIndex-1) + ", Length: " + srcLength); -// } - final int removals = indices.cardinality(); // true bits are items to remove - final Object result = Array.newInstance(array.getClass().getComponentType(), srcLength - removals); - int srcIndex = 0; - int destIndex = 0; - int count; - int set; - while ((set = indices.nextSetBit(srcIndex)) != -1) { - count = set - srcIndex; - if (count > 0) { - System.arraycopy(array, srcIndex, result, destIndex, count); - destIndex += count; - } - srcIndex = indices.nextClearBit(set); - } - count = srcLength - srcIndex; - if (count > 0) { - System.arraycopy(array, srcIndex, result, destIndex, count); - } - return result; - } - - /** - * Removes multiple array elements specified by index. - * @param array source - * @param indices to remove - * @return new array of same type minus elements specified by unique values of {@code indices} - * @since 3.0.1 - */ - // package protected for access by unit tests - static Object removeAll(final Object array, final int... indices) { final int length = getLength(array); int diff = 0; // number of distinct indexes, i.e. number of entries that will be removed - final int[] clonedIndices = clone(indices); - Arrays.sort(clonedIndices); - + final int[] clonedIndices = ArraySorter.sort(clone(indices)); // identify length of result array if (isNotEmpty(clonedIndices)) { int i = clonedIndices.length; @@ -5201,10 +5543,9 @@ static Object removeAll(final Object array, final int... indices) { prevIndex = index; } } - // create result array final Object result = Array.newInstance(array.getClass().getComponentType(), length - diff); - if (diff < length) { + if (diff < length && clonedIndices != null) { int end = length; // index just after last copy int dest = length - diff; // number of entries so far not copied for (int i = clonedIndices.length - 1; i >= 0; i--) { @@ -5213,7 +5554,7 @@ static Object removeAll(final Object array, final int... indices) { final int cp = end - index - 1; dest -= cp; System.arraycopy(array, index + 1, result, dest, cp); - // Afer this copy, we still have room for dest items. + // After this copy, we still have room for dest items. } end = index; } @@ -5225,16 +5566,14 @@ static Object removeAll(final Object array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll([1], 0)             = []
@@ -5245,12 +5584,10 @@ static Object removeAll(final Object array, final int... indices) {
      * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
      * 
* - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ public static short[] removeAll(final short[] array, final int... indices) { @@ -5258,29 +5595,25 @@ public static short[] removeAll(final short[] array, final int... indices) { } /** - *

Removes the elements at the specified positions from the specified array. - * All remaining elements are shifted to the left. - * - *

This method returns a new array with the same elements of the input - * array except those at the specified positions. The component - * type of the returned array is always the same as that of the input - * array. - * - *

If the input array is {@code null}, an IndexOutOfBoundsException - * will be thrown, because in that case no valid index can be specified. + * Removes the elements at the specified positions from the specified array. All remaining elements are shifted to the left. + *

+ * This method returns a new array with the same elements of the input array except those at the specified positions. The component type of the returned + * array is always the same as that of the input array. + *

+ *

+ * If the input array is {@code null}, then return {@code null}. + *

* *
      * ArrayUtils.removeAll(["a", "b", "c"], 0, 2) = ["b"]
      * ArrayUtils.removeAll(["a", "b", "c"], 1, 2) = ["a"]
      * 
* - * @param the component type of the array - * @param array the array to remove the element from, may not be {@code null} - * @param indices the positions of the elements to be removed - * @return A new array containing the existing elements except those - * at the specified positions. - * @throws IndexOutOfBoundsException if any index is out of range - * (index < 0 || index >= array.length), or if the array is {@code null}. + * @param the component type of the array. + * @param array The array to remove the element from, may not be {@code null}. + * @param indices The positions of the elements to be removed. + * @return A new array containing the existing elements except those at the specified positions or {@code null} if the input array is {@code null}. + * @throws IndexOutOfBoundsException if any index is out of range (index < 0 || index >= array.length). * @since 3.0.1 */ @SuppressWarnings("unchecked") // removeAll() always creates an array of the same type as its input @@ -5290,187 +5623,381 @@ public static T[] removeAll(final T[] array, final int... indices) { /** * Removes the occurrences of the specified element from the specified boolean array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(boolean[], boolean)}. */ + @Deprecated public static boolean[] removeAllOccurences(final boolean[] array, final boolean element) { - return (boolean[]) removeAll((Object) array, indexesOf(array, element)); + return (boolean[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified byte array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(byte[], byte)}. */ + @Deprecated public static byte[] removeAllOccurences(final byte[] array, final byte element) { - return (byte[]) removeAll((Object) array, indexesOf(array, element)); + return (byte[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified char array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(char[], char)}. */ + @Deprecated public static char[] removeAllOccurences(final char[] array, final char element) { - return (char[]) removeAll((Object) array, indexesOf(array, element)); + return (char[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified double array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(double[], double)}. */ + @Deprecated public static double[] removeAllOccurences(final double[] array, final double element) { - return (double[]) removeAll((Object) array, indexesOf(array, element)); + return (double[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified float array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(float[], float)}. + */ + @Deprecated + public static float[] removeAllOccurences(final float[] array, final float element) { + return (float[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified int array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(int[], int)}. + */ + @Deprecated + public static int[] removeAllOccurences(final int[] array, final int element) { + return (int[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified long array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(long[], long)}. + */ + @Deprecated + public static long[] removeAllOccurences(final long[] array, final long element) { + return (long[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified short array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(short[], short)}. + */ + @Deprecated + public static short[] removeAllOccurences(final short[] array, final short element) { + return (short[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param The type of object in the array, may be {@code null}. + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove, may be {@code null}. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.5 + * @deprecated Use {@link #removeAllOccurrences(Object[], Object)}. + */ + @Deprecated + public static T[] removeAllOccurences(final T[] array, final T element) { + return (T[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified boolean array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.10 + */ + public static boolean[] removeAllOccurrences(final boolean[] array, final boolean element) { + return (boolean[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified byte array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.10 + */ + public static byte[] removeAllOccurrences(final byte[] array, final byte element) { + return (byte[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified char array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

+ * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.10 + */ + public static char[] removeAllOccurrences(final char[] array, final char element) { + return (char[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified double array. + *

+ * All subsequent elements are shifted to the left (subtracts one from their indices). + * If the array doesn't contain such an element, no elements are removed from the array. + * {@code null} will be returned if the input array is {@code null}. + *

* + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. + * @return A new array containing the existing elements except the occurrences of the specified element. + * @since 3.10 + */ + public static double[] removeAllOccurrences(final double[] array, final double element) { + return (double[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes the occurrences of the specified element from the specified float array. *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. - * @since 3.5 + * @since 3.10 */ - public static float[] removeAllOccurences(final float[] array, final float element) { - return (float[]) removeAll((Object) array, indexesOf(array, element)); + public static float[] removeAllOccurrences(final float[] array, final float element) { + return (float[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified int array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. - * @since 3.5 + * @since 3.10 */ - public static int[] removeAllOccurences(final int[] array, final int element) { - return (int[]) removeAll((Object) array, indexesOf(array, element)); + public static int[] removeAllOccurrences(final int[] array, final int element) { + return (int[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified long array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. - * @since 3.5 + * @since 3.10 */ - public static long[] removeAllOccurences(final long[] array, final long element) { - return (long[]) removeAll((Object) array, indexesOf(array, element)); + public static long[] removeAllOccurrences(final long[] array, final long element) { + return (long[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified short array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param element the element to remove - * @param array the input array - * + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove. * @return A new array containing the existing elements except the occurrences of the specified element. - * @since 3.5 + * @since 3.10 */ - public static short[] removeAllOccurences(final short[] array, final short element) { - return (short[]) removeAll((Object) array, indexesOf(array, element)); + public static short[] removeAllOccurrences(final short[] array, final short element) { + return (short[]) removeAt(array, indexesOf(array, element)); } /** * Removes the occurrences of the specified element from the specified array. - * *

* All subsequent elements are shifted to the left (subtracts one from their indices). - * If the array doesn't contains such an element, no elements are removed from the array. + * If the array doesn't contain such an element, no elements are removed from the array. * {@code null} will be returned if the input array is {@code null}. *

* - * @param the type of object in the array - * @param element the element to remove - * @param array the input array - * + * @param The type of object in the array, may be {@code null}. + * @param array The input array, will not be modified, and may be {@code null}. + * @param element The element to remove, may be {@code null}. * @return A new array containing the existing elements except the occurrences of the specified element. - * @since 3.5 + * @since 3.10 */ - public static T[] removeAllOccurences(final T[] array, final T element) { - return (T[]) removeAll((Object) array, indexesOf(array, element)); + public static T[] removeAllOccurrences(final T[] array, final T element) { + return (T[]) removeAt(array, indexesOf(array, element)); + } + + /** + * Removes multiple array elements specified by indices. + * + * @param array The input array, will not be modified, and may be {@code null}. + * @param indices to remove. + * @return new array of same type minus elements specified by the set bits in {@code indices}. + */ + // package protected for access by unit tests + static Object removeAt(final Object array, final BitSet indices) { + if (array == null) { + return null; + } + final int srcLength = getLength(array); + // No need to check maxIndex here, because method only currently called from removeElements() + // which guarantee to generate only valid bit entries. +// final int maxIndex = indices.length(); +// if (maxIndex > srcLength) { +// throw new IndexOutOfBoundsException("Index: " + (maxIndex-1) + ", Length: " + srcLength); +// } + final int removals = indices.cardinality(); // true bits are items to remove + final Object result = Array.newInstance(array.getClass().getComponentType(), srcLength - removals); + int srcIndex = 0; + int destIndex = 0; + int count; + int set; + while ((set = indices.nextSetBit(srcIndex)) != -1) { + count = set - srcIndex; + if (count > 0) { + System.arraycopy(array, srcIndex, result, destIndex, count); + destIndex += count; + } + srcIndex = indices.nextClearBit(set); + } + count = srcLength - srcIndex; + if (count > 0) { + System.arraycopy(array, srcIndex, result, destIndex, count); + } + return result; } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, true)                = null
      * ArrayUtils.removeElement([], true)                  = []
@@ -5479,31 +6006,28 @@ public static  T[] removeAllOccurences(final T[] array, final T element) {
      * ArrayUtils.removeElement([true, false, true], true) = [false, true]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static boolean[] removeElement(final boolean[] array, final boolean element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 1)        = null
      * ArrayUtils.removeElement([], 1)          = []
@@ -5512,31 +6036,28 @@ public static boolean[] removeElement(final boolean[] array, final boolean eleme
      * ArrayUtils.removeElement([1, 0, 1], 1)   = [0, 1]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static byte[] removeElement(final byte[] array, final byte element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 'a')            = null
      * ArrayUtils.removeElement([], 'a')              = []
@@ -5545,31 +6066,28 @@ public static byte[] removeElement(final byte[] array, final byte element) {
      * ArrayUtils.removeElement(['a', 'b', 'a'], 'a') = ['b', 'a']
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static char[] removeElement(final char[] array, final char element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 1.1)            = null
      * ArrayUtils.removeElement([], 1.1)              = []
@@ -5578,31 +6096,28 @@ public static char[] removeElement(final char[] array, final char element) {
      * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static double[] removeElement(final double[] array, final double element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 1.1)            = null
      * ArrayUtils.removeElement([], 1.1)              = []
@@ -5611,31 +6126,28 @@ public static double[] removeElement(final double[] array, final double element)
      * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static float[] removeElement(final float[] array, final float element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 1)      = null
      * ArrayUtils.removeElement([], 1)        = []
@@ -5644,31 +6156,28 @@ public static float[] removeElement(final float[] array, final float element) {
      * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static int[] removeElement(final int[] array, final int element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 1)      = null
      * ArrayUtils.removeElement([], 1)        = []
@@ -5677,31 +6186,28 @@ public static int[] removeElement(final int[] array, final int element) {
      * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static long[] removeElement(final long[] array, final long element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, 1)      = null
      * ArrayUtils.removeElement([], 1)        = []
@@ -5710,31 +6216,28 @@ public static long[] removeElement(final long[] array, final long element) {
      * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param array The input array, may be {@code null}. + * @param element The element to be removed. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static short[] removeElement(final short[] array, final short element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes the first occurrence of the specified element from the + * Removes the first occurrence of the specified element from the * specified array. All subsequent elements are shifted to the left - * (subtracts one from their indices). If the array doesn't contains + * (subtracts one from their indices). If the array doesn't contain * such an element, no elements are removed from the array. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except the first occurrence of the specified element. The component * type of the returned array is always the same as that of the input * array. - * + *

*
      * ArrayUtils.removeElement(null, "a")            = null
      * ArrayUtils.removeElement([], "a")              = []
@@ -5743,33 +6246,30 @@ public static short[] removeElement(final short[] array, final short element) {
      * ArrayUtils.removeElement(["a", "b", "a"], "a") = ["b", "a"]
      * 
* - * @param the component type of the array - * @param array the array to remove the element from, may be {@code null} - * @param element the element to be removed + * @param The component type of the array + * @param array The input array, may be {@code null}. + * @param element The element to be removed, may be {@code null}. * @return A new array containing the existing elements except the first * occurrence of the specified element. * @since 2.1 */ public static T[] removeElement(final T[] array, final Object element) { final int index = indexOf(array, element); - if (index == INDEX_NOT_FOUND) { - return clone(array); - } - return remove(array, index); + return index == INDEX_NOT_FOUND ? clone(array) : remove(array, index); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, true, false)               = null
      * ArrayUtils.removeElements([], true, false)                 = []
@@ -5779,8 +6279,8 @@ public static  T[] removeElement(final T[] array, final Object element) {
      * ArrayUtils.removeElements([true, false, true], true, true) = [false]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -5791,13 +6291,7 @@ public static boolean[] removeElements(final boolean[] array, final boolean... v } final HashMap occurrences = new HashMap<>(2); // only two possible values here for (final boolean v : values) { - final Boolean boxed = Boolean.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Boolean.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -5810,21 +6304,21 @@ public static boolean[] removeElements(final boolean[] array, final boolean... v toRemove.set(i); } } - return (boolean[]) removeAll(array, toRemove); + return (boolean[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -5834,8 +6328,8 @@ public static boolean[] removeElements(final boolean[] array, final boolean... v
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -5844,15 +6338,9 @@ public static byte[] removeElements(final byte[] array, final byte... values) { if (isEmpty(array) || isEmpty(values)) { return clone(array); } - final Map occurrences = new HashMap<>(values.length); + final HashMap occurrences = new HashMap<>(values.length); for (final byte v : values) { - final Byte boxed = Byte.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Byte.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -5865,21 +6353,21 @@ public static byte[] removeElements(final byte[] array, final byte... values) { toRemove.set(i); } } - return (byte[]) removeAll(array, toRemove); + return (byte[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -5889,8 +6377,8 @@ public static byte[] removeElements(final byte[] array, final byte... values) {
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -5901,13 +6389,7 @@ public static char[] removeElements(final char[] array, final char... values) { } final HashMap occurrences = new HashMap<>(values.length); for (final char v : values) { - final Character boxed = Character.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Character.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -5920,21 +6402,21 @@ public static char[] removeElements(final char[] array, final char... values) { toRemove.set(i); } } - return (char[]) removeAll(array, toRemove); + return (char[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -5944,8 +6426,8 @@ public static char[] removeElements(final char[] array, final char... values) {
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -5956,13 +6438,7 @@ public static double[] removeElements(final double[] array, final double... valu } final HashMap occurrences = new HashMap<>(values.length); for (final double v : values) { - final Double boxed = Double.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Double.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -5975,21 +6451,21 @@ public static double[] removeElements(final double[] array, final double... valu toRemove.set(i); } } - return (double[]) removeAll(array, toRemove); + return (double[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -5999,8 +6475,8 @@ public static double[] removeElements(final double[] array, final double... valu
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -6011,13 +6487,7 @@ public static float[] removeElements(final float[] array, final float... values) } final HashMap occurrences = new HashMap<>(values.length); for (final float v : values) { - final Float boxed = Float.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Float.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -6030,21 +6500,21 @@ public static float[] removeElements(final float[] array, final float... values) toRemove.set(i); } } - return (float[]) removeAll(array, toRemove); + return (float[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -6054,8 +6524,8 @@ public static float[] removeElements(final float[] array, final float... values)
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -6066,13 +6536,7 @@ public static int[] removeElements(final int[] array, final int... values) { } final HashMap occurrences = new HashMap<>(values.length); for (final int v : values) { - final Integer boxed = Integer.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Integer.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -6085,21 +6549,21 @@ public static int[] removeElements(final int[] array, final int... values) { toRemove.set(i); } } - return (int[]) removeAll(array, toRemove); + return (int[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -6109,8 +6573,8 @@ public static int[] removeElements(final int[] array, final int... values) {
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -6121,13 +6585,7 @@ public static long[] removeElements(final long[] array, final long... values) { } final HashMap occurrences = new HashMap<>(values.length); for (final long v : values) { - final Long boxed = Long.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Long.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -6140,21 +6598,21 @@ public static long[] removeElements(final long[] array, final long... values) { toRemove.set(i); } } - return (long[]) removeAll(array, toRemove); + return (long[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, 1, 2)      = null
      * ArrayUtils.removeElements([], 1, 2)        = []
@@ -6164,8 +6622,8 @@ public static long[] removeElements(final long[] array, final long... values) {
      * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
      * 
* - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -6176,13 +6634,7 @@ public static short[] removeElements(final short[] array, final short... values) } final HashMap occurrences = new HashMap<>(values.length); for (final short v : values) { - final Short boxed = Short.valueOf(v); - final MutableInt count = occurrences.get(boxed); - if (count == null) { - occurrences.put(boxed, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, Short.valueOf(v)); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -6195,21 +6647,21 @@ public static short[] removeElements(final short[] array, final short... values) toRemove.set(i); } } - return (short[]) removeAll(array, toRemove); + return (short[]) removeAt(array, toRemove); } /** - *

Removes occurrences of specified elements, in specified quantities, + * Removes occurrences of specified elements, in specified quantities, * from the specified array. All subsequent elements are shifted left. * For any element-to-be-removed specified in greater quantities than * contained in the original array, no change occurs beyond the * removal of the existing matching items. - * - *

This method returns a new array with the same elements of the input + *

+ * This method returns a new array with the same elements of the input * array except for the earliest-encountered occurrences of the specified * elements. The component type of the returned array is always the same * as that of the input array. - * + *

*
      * ArrayUtils.removeElements(null, "a", "b")            = null
      * ArrayUtils.removeElements([], "a", "b")              = []
@@ -6219,9 +6671,9 @@ public static short[] removeElements(final short[] array, final short... values)
      * ArrayUtils.removeElements(["a", "b", "a"], "a", "a") = ["b"]
      * 
* - * @param the component type of the array - * @param array the array to remove the element from, may be {@code null} - * @param values the elements to be removed + * @param The component type of the array + * @param array The input array, will not be modified, and may be {@code null}. + * @param values The values to be removed. * @return A new array containing the existing elements except the * earliest-encountered occurrences of the specified elements. * @since 3.0.1 @@ -6233,12 +6685,7 @@ public static T[] removeElements(final T[] array, final T... values) { } final HashMap occurrences = new HashMap<>(values.length); for (final T v : values) { - final MutableInt count = occurrences.get(v); - if (count == null) { - occurrences.put(v, new MutableInt(1)); - } else { - count.increment(); - } + increment(occurrences, v); } final BitSet toRemove = new BitSet(); for (int i = 0; i < array.length; i++) { @@ -6252,33 +6699,32 @@ public static T[] removeElements(final T[] array, final T... values) { } } @SuppressWarnings("unchecked") // removeAll() always creates an array of the same type as its input - final T[] result = (T[]) removeAll(array, toRemove); + final T[] result = (T[]) removeAt(array, toRemove); return result; } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final boolean[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* * @param array - * the array to reverse, may be {@code null} + * the array to reverse, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6291,8 +6737,8 @@ public static void reverse(final boolean[] array, final int startIndexInclusive, if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; boolean tmp; while (j > i) { tmp = array[j]; @@ -6304,42 +6750,37 @@ public static void reverse(final boolean[] array, final int startIndexInclusive, } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final byte[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* - * @param array - * the array to reverse, may be {@code null} - * @param startIndexInclusive - * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no - * change. - * @param endIndexExclusive - * elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no - * change. Overvalue (>array.length) is demoted to array length. + * @param array The array to reverse, may be {@code null}. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no change. + * @param endIndexExclusive elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no change. Overvalue + * (>array.length) is demoted to array length. * @since 3.2 */ public static void reverse(final byte[] array, final int startIndexInclusive, final int endIndexExclusive) { if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; byte tmp; while (j > i) { tmp = array[j]; @@ -6351,42 +6792,37 @@ public static void reverse(final byte[] array, final int startIndexInclusive, fi } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final char[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* - * @param array - * the array to reverse, may be {@code null} - * @param startIndexInclusive - * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no - * change. - * @param endIndexExclusive - * elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no - * change. Overvalue (>array.length) is demoted to array length. + * @param array The array to reverse, may be {@code null}. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no change. + * @param endIndexExclusive elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no change. Overvalue + * (>array.length) is demoted to array length. * @since 3.2 */ public static void reverse(final char[] array, final int startIndexInclusive, final int endIndexExclusive) { if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; char tmp; while (j > i) { tmp = array[j]; @@ -6398,42 +6834,37 @@ public static void reverse(final char[] array, final int startIndexInclusive, fi } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null} */ public static void reverse(final double[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* - * @param array - * the array to reverse, may be {@code null} - * @param startIndexInclusive - * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no - * change. - * @param endIndexExclusive - * elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no - * change. Overvalue (>array.length) is demoted to array length. + * @param array The array to reverse, may be {@code null}. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no change. + * @param endIndexExclusive elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no change. Overvalue + * (>array.length) is demoted to array length. * @since 3.2 */ public static void reverse(final double[] array, final int startIndexInclusive, final int endIndexExclusive) { if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; double tmp; while (j > i) { tmp = array[j]; @@ -6445,42 +6876,37 @@ public static void reverse(final double[] array, final int startIndexInclusive, } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final float[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* - * @param array - * the array to reverse, may be {@code null} - * @param startIndexInclusive - * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no - * change. - * @param endIndexExclusive - * elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no - * change. Overvalue (>array.length) is demoted to array length. + * @param array The array to reverse, may be {@code null}. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no change. + * @param endIndexExclusive elements up to endIndex-1 are reversed in the array. Undervalue (< start index) results in no change. Overvalue + * (>array.length) is demoted to array length. * @since 3.2 */ public static void reverse(final float[] array, final int startIndexInclusive, final int endIndexExclusive) { if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; float tmp; while (j > i) { tmp = array[j]; @@ -6492,28 +6918,27 @@ public static void reverse(final float[] array, final int startIndexInclusive, f } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final int[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* * @param array - * the array to reverse, may be {@code null} + * the array to reverse, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6526,8 +6951,8 @@ public static void reverse(final int[] array, final int startIndexInclusive, fin if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; int tmp; while (j > i) { tmp = array[j]; @@ -6539,28 +6964,27 @@ public static void reverse(final int[] array, final int startIndexInclusive, fin } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final long[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* * @param array - * the array to reverse, may be {@code null} + * the array to reverse, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6573,8 +6997,8 @@ public static void reverse(final long[] array, final int startIndexInclusive, fi if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; long tmp; while (j > i) { tmp = array[j]; @@ -6585,33 +7009,31 @@ public static void reverse(final long[] array, final int startIndexInclusive, fi } } - // Reverse - //----------------------------------------------------------------------- /** - *

Reverses the order of the given array. - * - *

There is no special handling for multi-dimensional arrays. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * There is no special handling for multi-dimensional arrays. + *

+ *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final Object[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* * @param array - * the array to reverse, may be {@code null} + * the array to reverse, may be {@code null}. * @param startIndexInclusive * the starting index. Under value (<0) is promoted to 0, over value (>array.length) results in no * change. @@ -6624,8 +7046,8 @@ public static void reverse(final Object[] array, final int startIndexInclusive, if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; Object tmp; while (j > i) { tmp = array[j]; @@ -6637,28 +7059,27 @@ public static void reverse(final Object[] array, final int startIndexInclusive, } /** - *

Reverses the order of the given array. - * - *

This method does nothing for a {@code null} input array. + * Reverses the order of the given array. + *

+ * This method does nothing for a {@code null} input array. + *

* - * @param array the array to reverse, may be {@code null} + * @param array The array to reverse, may be {@code null}. */ public static void reverse(final short[] array) { - if (array == null) { - return; + if (array != null) { + reverse(array, 0, array.length); } - reverse(array, 0, array.length); } /** - *

* Reverses the order of the given array in the given range. - * *

* This method does nothing for a {@code null} input array. + *

* * @param array - * the array to reverse, may be {@code null} + * the array to reverse, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6671,8 +7092,8 @@ public static void reverse(final short[] array, final int startIndexInclusive, f if (array == null) { return; } - int i = startIndexInclusive < 0 ? 0 : startIndexInclusive; - int j = Math.min(array.length, endIndexExclusive) - 1; + int i = Math.max(startIndexInclusive, 0); + int j = max0(Math.min(array.length, endIndexExclusive)) - 1; short tmp; while (j > i) { tmp = array[j]; @@ -6683,23 +7104,64 @@ public static void reverse(final short[] array, final int startIndexInclusive, f } } + /** + * Sets all elements of the specified array, using the provided generator supplier to compute each element. + *

+ * If the generator supplier throws an exception, it is relayed to the caller and the array is left in an indeterminate + * state. + *

+ * + * @param type of elements of the array, may be {@code null}. + * @param array array to be initialized, may be {@code null}. + * @param generator A function accepting an index and producing the desired value for that position. + * @return The input array + * @since 3.13.0 + */ + public static T[] setAll(final T[] array, final IntFunction generator) { + if (array != null && generator != null) { + Arrays.setAll(array, generator); + } + return array; + } + + /** + * Sets all elements of the specified array, using the provided generator supplier to compute each element. + *

+ * If the generator supplier throws an exception, it is relayed to the caller and the array is left in an indeterminate + * state. + *

+ * + * @param type of elements of the array, may be {@code null}. + * @param array array to be initialized, may be {@code null}. + * @param generator A function accepting an index and producing the desired value for that position. + * @return The input array + * @since 3.13.0 + */ + public static T[] setAll(final T[] array, final Supplier generator) { + if (array != null && generator != null) { + for (int i = 0; i < array.length; i++) { + array[i] = generator.get(); + } + } + return array; + } + /** * Shifts the order of the given boolean array. * *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final boolean[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -6709,7 +7171,7 @@ public static void shift(final boolean[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6722,18 +7184,11 @@ public static void shift(final boolean[] array, final int offset) { * @since 3.5 */ public static void shift(final boolean[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -6745,18 +7200,17 @@ public static void shift(final boolean[] array, int startIndexInclusive, int end // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -6768,17 +7222,16 @@ public static void shift(final boolean[] array, int startIndexInclusive, int end *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final byte[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -6788,7 +7241,7 @@ public static void shift(final byte[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6801,18 +7254,11 @@ public static void shift(final byte[] array, final int offset) { * @since 3.5 */ public static void shift(final byte[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { - return; - } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -6824,18 +7270,17 @@ public static void shift(final byte[] array, int startIndexInclusive, int endInd // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -6847,17 +7292,16 @@ public static void shift(final byte[] array, int startIndexInclusive, int endInd *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final char[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -6867,7 +7311,7 @@ public static void shift(final char[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6880,18 +7324,11 @@ public static void shift(final char[] array, final int offset) { * @since 3.5 */ public static void shift(final char[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -6903,18 +7340,17 @@ public static void shift(final char[] array, int startIndexInclusive, int endInd // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -6926,17 +7362,16 @@ public static void shift(final char[] array, int startIndexInclusive, int endInd *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final double[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -6946,7 +7381,7 @@ public static void shift(final double[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -6959,18 +7394,11 @@ public static void shift(final double[] array, final int offset) { * @since 3.5 */ public static void shift(final double[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -6982,18 +7410,17 @@ public static void shift(final double[] array, int startIndexInclusive, int endI // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -7005,17 +7432,16 @@ public static void shift(final double[] array, int startIndexInclusive, int endI *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final float[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -7025,7 +7451,7 @@ public static void shift(final float[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -7038,18 +7464,11 @@ public static void shift(final float[] array, final int offset) { * @since 3.5 */ public static void shift(final float[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -7061,18 +7480,17 @@ public static void shift(final float[] array, int startIndexInclusive, int endIn // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -7084,17 +7502,16 @@ public static void shift(final float[] array, int startIndexInclusive, int endIn *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final int[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -7104,7 +7521,7 @@ public static void shift(final int[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -7117,18 +7534,11 @@ public static void shift(final int[] array, final int offset) { * @since 3.5 */ public static void shift(final int[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { - return; - } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -7140,18 +7550,17 @@ public static void shift(final int[] array, int startIndexInclusive, int endInde // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -7163,17 +7572,16 @@ public static void shift(final int[] array, int startIndexInclusive, int endInde *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final long[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -7183,7 +7591,7 @@ public static void shift(final long[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -7196,18 +7604,11 @@ public static void shift(final long[] array, final int offset) { * @since 3.5 */ public static void shift(final long[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -7219,42 +7620,38 @@ public static void shift(final long[] array, int startIndexInclusive, int endInd // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } } - // Shift - //----------------------------------------------------------------------- /** * Shifts the order of the given array. * *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final Object[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -7264,7 +7661,7 @@ public static void shift(final Object[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -7277,18 +7674,11 @@ public static void shift(final Object[] array, final int offset) { * @since 3.5 */ public static void shift(final Object[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -7300,18 +7690,17 @@ public static void shift(final Object[] array, int startIndexInclusive, int endI // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } @@ -7323,17 +7712,16 @@ public static void shift(final Object[] array, int startIndexInclusive, int endI *

There is no special handling for multi-dimensional arrays. This method * does nothing for {@code null} or empty input arrays.

* - * @param array the array to shift, may be {@code null} + * @param array The array to shift, may be {@code null}. * @param offset * The number of positions to rotate the elements. If the offset is larger than the number of elements to * rotate, than the effective offset is modulo the number of elements to rotate. * @since 3.5 */ public static void shift(final short[] array, final int offset) { - if (array == null) { - return; + if (array != null) { + shift(array, 0, array.length, offset); } - shift(array, 0, array.length, offset); } /** @@ -7343,7 +7731,7 @@ public static void shift(final short[] array, final int offset) { * does nothing for {@code null} or empty input arrays.

* * @param array - * the array to shift, may be {@code null} + * the array to shift, may be {@code null}. * @param startIndexInclusive * the starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in no * change. @@ -7356,18 +7744,11 @@ public static void shift(final short[] array, final int offset) { * @since 3.5 */ public static void shift(final short[] array, int startIndexInclusive, int endIndexExclusive, int offset) { - if (array == null) { - return; - } - if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { + if (array == null || startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) { return; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive >= array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = Math.min(endIndexExclusive, array.length); int n = endIndexExclusive - startIndexInclusive; if (n <= 1) { return; @@ -7379,265 +7760,392 @@ public static void shift(final short[] array, int startIndexInclusive, int endIn // For algorithm explanations and proof of O(n) time complexity and O(1) space complexity // see https://beradrian.wordpress.com/2015/04/07/shift-an-array-in-on-in-place/ while (n > 1 && offset > 0) { - final int n_offset = n - offset; - - if (offset > n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset); + final int nOffset = n - offset; + if (offset > nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + n - nOffset, nOffset); n = offset; - offset -= n_offset; - } else if (offset < n_offset) { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + offset -= nOffset; + } else if (offset < nOffset) { + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); startIndexInclusive += offset; - n = n_offset; + n = nOffset; } else { - swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset); + swap(array, startIndexInclusive, startIndexInclusive + nOffset, offset); break; } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final boolean[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final boolean[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final byte[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final byte[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final char[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final char[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final double[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final double[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final float[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final float[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final int[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final int[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final long[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final long[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final Object[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final Object[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. + *

+ * This method uses the current {@link ThreadLocalRandom} as its random number generator. + *

+ *

+ * Instances of {@link ThreadLocalRandom} are not cryptographically secure. For security-sensitive applications, consider using a {@code shuffle} method + * with a {@link SecureRandom} argument. + *

* - * @param array the array to shuffle + * @param array The array to shuffle. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final short[] array) { - shuffle(array, new Random()); + shuffle(array, random()); } /** - * Randomly permutes the elements of the specified array using the Fisher-Yates algorithm. + * Shuffles randomly the elements of the specified array using the Fisher-Yates shuffle + * algorithm. * - * @param array the array to shuffle - * @param random the source of randomness used to permute the elements + * @param array The array to shuffle, no-op if {@code null}. + * @param random The source of randomness used to permute the elements, no-op if {@code null}. * @see Fisher-Yates shuffle algorithm * @since 3.6 */ public static void shuffle(final short[] array, final Random random) { - for (int i = array.length; i > 1; i--) { - swap(array, i - 1, random.nextInt(i), 1); + if (array != null && random != null) { + for (int i = array.length; i > 1; i--) { + swap(array, i - 1, random.nextInt(i), 1); + } } } /** - *

Produces a new {@code boolean} array containing the elements - * between the start and end indices. + * Tests whether the given data array starts with an expected array, for example, signature bytes. + *

+ * If both arrays are null, the method returns true. The method return false when one array is null and the other not. + *

* - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * @param data The data to search, maybe larger than the expected data. + * @param expected The expected data to find. + * @return whether a match was found. + * @since 3.18.0 + */ + public static boolean startsWith(final byte[] data, final byte[] expected) { + if (data == expected) { + return true; + } + if (data == null || expected == null) { + return false; + } + final int dataLen = data.length; + if (expected.length > dataLen) { + return false; + } + if (expected.length == dataLen) { + // delegate to Arrays.equals() which has optimizations on Java > 8 + return Arrays.equals(data, expected); + } + // Once we are on Java 9+ we can delegate to Arrays here as well (or not). + for (int i = 0; i < expected.length; i++) { + if (data[i] != expected[i]) { + return false; + } + } + return true; + } + + /** + * Produces a new {@code boolean} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(boolean[], int, int) */ @@ -7645,39 +8153,26 @@ public static boolean[] subarray(final boolean[] array, int startIndexInclusive, if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_BOOLEAN_ARRAY; } - - final boolean[] subarray = new boolean[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, boolean[]::new); } /** - *

Produces a new {@code byte} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code byte} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(byte[], int, int) */ @@ -7685,39 +8180,26 @@ public static byte[] subarray(final byte[] array, int startIndexInclusive, int e if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_BYTE_ARRAY; } - - final byte[] subarray = new byte[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, byte[]::new); } /** - *

Produces a new {@code char} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code char} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(char[], int, int) */ @@ -7725,39 +8207,26 @@ public static char[] subarray(final char[] array, int startIndexInclusive, int e if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_CHAR_ARRAY; } - - final char[] subarray = new char[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, char[]::new); } /** - *

Produces a new {@code double} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code double} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(double[], int, int) */ @@ -7765,39 +8234,26 @@ public static double[] subarray(final double[] array, int startIndexInclusive, i if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_DOUBLE_ARRAY; } - - final double[] subarray = new double[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, double[]::new); } /** - *

Produces a new {@code float} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code float} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(float[], int, int) */ @@ -7805,39 +8261,26 @@ public static float[] subarray(final float[] array, int startIndexInclusive, int if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_FLOAT_ARRAY; } - - final float[] subarray = new float[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, float[]::new); } /** - *

Produces a new {@code int} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code int} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(int[], int, int) */ @@ -7845,39 +8288,26 @@ public static int[] subarray(final int[] array, int startIndexInclusive, int end if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_INT_ARRAY; } - - final int[] subarray = new int[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, int[]::new); } /** - *

Produces a new {@code long} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code long} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(long[], int, int) */ @@ -7885,39 +8315,26 @@ public static long[] subarray(final long[] array, int startIndexInclusive, int e if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_LONG_ARRAY; } - - final long[] subarray = new long[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, long[]::new); } /** - *

Produces a new {@code short} array containing the elements - * between the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. + * Produces a new {@code short} array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

* - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(short[], int, int) */ @@ -7925,50 +8342,36 @@ public static short[] subarray(final short[] array, int startIndexInclusive, int if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; if (newSize <= 0) { return EMPTY_SHORT_ARRAY; } - - final short[] subarray = new short[newSize]; - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, short[]::new); } - // Subarrays - //----------------------------------------------------------------------- /** - *

Produces a new array containing the elements between - * the start and end indices. - * - *

The start index is inclusive, the end index exclusive. - * Null array input produces null output. - * - *

The component type of the subarray is always the same as - * that of the input array. Thus, if the input is an array of type - * {@code Date}, the following usage is envisaged: + * Produces a new array containing the elements between the start and end indices. + *

+ * The start index is inclusive, the end index exclusive. Null array input produces null output. + *

+ *

+ * The component type of the subarray is always the same as that of the input array. Thus, if the input is an array of type {@link Date}, the following + * usage is envisaged: + *

* *
+     *
      * Date[] someDates = (Date[]) ArrayUtils.subarray(allDates, 2, 5);
      * 
* - * @param the component type of the array - * @param array the array - * @param startIndexInclusive the starting index. Undervalue (<0) - * is promoted to 0, overvalue (>array.length) results - * in an empty array. - * @param endIndexExclusive elements up to endIndex-1 are present in the - * returned subarray. Undervalue (< startIndex) produces - * empty array, overvalue (>array.length) is demoted to - * array length. - * @return a new array containing the elements between - * the start and end indices. + * @param the component type of the array. + * @param array The input array. + * @param startIndexInclusive The starting index. Undervalue (<0) is promoted to 0, overvalue (>array.length) results in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the returned subarray. Undervalue (< startIndex) produces empty array, overvalue + * (>array.length) is demoted to array length. + * @return A new array containing the elements between the start and end indices. * @since 2.1 * @see Arrays#copyOfRange(Object[], int, int) */ @@ -7976,24 +8379,14 @@ public static T[] subarray(final T[] array, int startIndexInclusive, int end if (array == null) { return null; } - if (startIndexInclusive < 0) { - startIndexInclusive = 0; - } - if (endIndexExclusive > array.length) { - endIndexExclusive = array.length; - } + startIndexInclusive = max0(startIndexInclusive); + endIndexExclusive = max0(Math.min(endIndexExclusive, array.length)); final int newSize = endIndexExclusive - startIndexInclusive; - final Class type = array.getClass().getComponentType(); + final Class type = getComponentType(array); if (newSize <= 0) { - @SuppressWarnings("unchecked") // OK, because array is of type T - final T[] emptyArray = (T[]) Array.newInstance(type, 0); - return emptyArray; + return newInstance(type, 0); } - @SuppressWarnings("unchecked") // OK, because array is of type T - final - T[] subarray = (T[]) Array.newInstance(type, newSize); - System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); - return subarray; + return arraycopy(array, startIndexInclusive, 0, newSize, () -> newInstance(type, newSize)); } /** @@ -8012,15 +8405,12 @@ public static T[] subarray(final T[] array, int startIndexInclusive, int end *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final boolean[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8042,22 +8432,18 @@ public static void swap(final boolean[] array, final int offset1, final int offs *
  • ArrayUtils.swap([true, false, true, false], 0, 3, 3) -> [false, false, true, true]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final boolean[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final boolean aux = array[offset1]; @@ -8066,7 +8452,6 @@ public static void swap(final boolean[] array, int offset1, int offset2, int len } } - /** * Swaps two elements in the given byte array. * @@ -8083,15 +8468,12 @@ public static void swap(final boolean[] array, int offset1, int offset2, int len *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final byte[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8113,22 +8495,18 @@ public static void swap(final byte[] array, final int offset1, final int offset2 *
  • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final byte[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final byte aux = array[offset1]; @@ -8153,15 +8531,12 @@ public static void swap(final byte[] array, int offset1, int offset2, int len) { *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final char[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8183,22 +8558,18 @@ public static void swap(final char[] array, final int offset1, final int offset2 *
  • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final char[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final char aux = array[offset1]; @@ -8223,15 +8594,12 @@ public static void swap(final char[] array, int offset1, int offset2, int len) { *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final double[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8253,22 +8621,18 @@ public static void swap(final double[] array, final int offset1, final int offse *
  • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final double[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final double aux = array[offset1]; @@ -8293,15 +8657,12 @@ public static void swap(final double[] array, int offset1, int offset2, int len *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final float[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8323,22 +8684,18 @@ public static void swap(final float[] array, final int offset1, final int offset *
  • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final float[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final float aux = array[offset1]; @@ -8364,15 +8721,12 @@ public static void swap(final float[] array, int offset1, int offset2, int len) *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final int[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8394,22 +8748,18 @@ public static void swap(final int[] array, final int offset1, final int offset2) *
  • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final int[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final int aux = array[offset1]; @@ -8434,16 +8784,12 @@ public static void swap(final int[] array, int offset1, int offset2, int len) { *
  • ArrayUtils.swap([true, false, true], -1, 1) -> [false, true, true]
  • * * - * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final long[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8465,22 +8811,18 @@ public static void swap(final long[] array, final int offset1, final int offset2 *
  • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final long[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final long aux = array[offset1]; @@ -8489,8 +8831,6 @@ public static void swap(final long[] array, int offset1, int offset2, int len) } } - // Swap - //----------------------------------------------------------------------- /** * Swaps two elements in the given array. * @@ -8507,15 +8847,12 @@ public static void swap(final long[] array, int offset1, int offset2, int len) *
  • ArrayUtils.swap(["1", "2", "3"], -1, 1) -> ["2", "1", "3"]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final Object[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } @@ -8537,22 +8874,18 @@ public static void swap(final Object[] array, final int offset1, final int offse *
  • ArrayUtils.swap(["1", "2", "3", "4"], 0, 3, 3) -> ["4", "2", "3", "1"]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. * @since 3.5 */ public static void swap(final Object[] array, int offset1, int offset2, int len) { if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { return; } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } + offset1 = max0(offset1); + offset2 = max0(offset2); len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); for (int i = 0; i < len; i++, offset1++, offset2++) { final Object aux = array[offset1]; @@ -8577,70 +8910,61 @@ public static void swap(final Object[] array, int offset1, int offset2, int len *
  • ArrayUtils.swap([1, 2, 3], -1, 1) -> [2, 1, 3]
  • * * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element to swap - * @param offset2 the index of the second element to swap + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element to swap. + * @param offset2 The index of the second element to swap. * @since 3.5 */ public static void swap(final short[] array, final int offset1, final int offset2) { - if (isEmpty(array)) { - return; - } swap(array, offset1, offset2, 1); } /** - * Swaps a series of elements in the given short array. - * - *

    This method does nothing for a {@code null} or empty input array or - * for overflow indices. Negative indices are promoted to 0(zero). If any - * of the sub-arrays to swap falls outside of the given array, then the - * swap is stopped at the end of the array and as many as possible elements - * are swapped.

    - * - * Examples: - *
      - *
    • ArrayUtils.swap([1, 2, 3, 4], 0, 2, 1) -> [3, 2, 1, 4]
    • - *
    • ArrayUtils.swap([1, 2, 3, 4], 0, 0, 1) -> [1, 2, 3, 4]
    • - *
    • ArrayUtils.swap([1, 2, 3, 4], 2, 0, 2) -> [3, 4, 1, 2]
    • - *
    • ArrayUtils.swap([1, 2, 3, 4], -3, 2, 2) -> [3, 4, 1, 2]
    • - *
    • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
    • - *
    - * - * @param array the array to swap, may be {@code null} - * @param offset1 the index of the first element in the series to swap - * @param offset2 the index of the second element in the series to swap - * @param len the number of elements to swap starting with the given indices - * @since 3.5 - */ - public static void swap(final short[] array, int offset1, int offset2, int len) { - if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { - return; - } - if (offset1 < 0) { - offset1 = 0; - } - if (offset2 < 0) { - offset2 = 0; - } - if (offset1 == offset2) { - return; - } - len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); - for (int i = 0; i < len; i++, offset1++, offset2++) { - final short aux = array[offset1]; - array[offset1] = array[offset2]; - array[offset2] = aux; - } + * Swaps a series of elements in the given short array. + * + *

    This method does nothing for a {@code null} or empty input array or + * for overflow indices. Negative indices are promoted to 0(zero). If any + * of the sub-arrays to swap falls outside of the given array, then the + * swap is stopped at the end of the array and as many as possible elements + * are swapped.

    + * + * Examples: + *
      + *
    • ArrayUtils.swap([1, 2, 3, 4], 0, 2, 1) -> [3, 2, 1, 4]
    • + *
    • ArrayUtils.swap([1, 2, 3, 4], 0, 0, 1) -> [1, 2, 3, 4]
    • + *
    • ArrayUtils.swap([1, 2, 3, 4], 2, 0, 2) -> [3, 4, 1, 2]
    • + *
    • ArrayUtils.swap([1, 2, 3, 4], -3, 2, 2) -> [3, 4, 1, 2]
    • + *
    • ArrayUtils.swap([1, 2, 3, 4], 0, 3, 3) -> [4, 2, 3, 1]
    • + *
    + * + * @param array The array to swap, may be {@code null}. + * @param offset1 The index of the first element in the series to swap. + * @param offset2 The index of the second element in the series to swap. + * @param len The number of elements to swap starting with the given indices. + * @since 3.5 + */ + public static void swap(final short[] array, int offset1, int offset2, int len) { + if (isEmpty(array) || offset1 >= array.length || offset2 >= array.length) { + return; + } + offset1 = max0(offset1); + offset2 = max0(offset2); + if (offset1 == offset2) { + return; + } + len = Math.min(Math.min(len, array.length - offset1), array.length - offset2); + for (int i = 0; i < len; i++, offset1++, offset2++) { + final short aux = array[offset1]; + array[offset1] = array[offset2]; + array[offset2] = aux; } + } - // Generic array - //----------------------------------------------------------------------- /** - *

    Create a type-safe generic array. - * - *

    The Java language does not allow an array to be created from a generic type: - * + * Create a type-safe generic array. + *

    + * The Java language does not allow an array to be created from a generic type: + *

    *
         public static <T> T[] createAnArray(int size) {
             return new T[size]; // compiler error here
    @@ -8649,60 +8973,56 @@ public static void swap(final short[] array,  int offset1, int offset2, int len)
             return (T[]) new Object[size]; // ClassCastException at runtime
         }
          * 
    - * - *

    Therefore new arrays of generic types can be created with this method. + *

    + * Therefore new arrays of generic types can be created with this method. * For example, an array of Strings can be created: - * - *

    -    String[] array = ArrayUtils.toArray("1", "2");
    -    String[] emptyArray = ArrayUtils.<String>toArray();
    -     * 
    - * - *

    The method is typically used in scenarios, where the caller itself uses generic types + *

    + *
    {@code
    +     * String[] array = ArrayUtils.toArray("1", "2");
    +     * String[] emptyArray = ArrayUtils.toArray();
    +     * }
    + *

    + * The method is typically used in scenarios, where the caller itself uses generic types * that have to be combined into an array. - * - *

    Note, this method makes only sense to provide arguments of the same type so that the + *

    + *

    + * Note, this method makes only sense to provide arguments of the same type so that the * compiler can deduce the type of the array itself. While it is possible to select the * type explicitly like in - * {@code Number[] array = ArrayUtils.<Number>toArray(Integer.valueOf(42), Double.valueOf(Math.PI))}, + * {@code Number[] array = ArrayUtils.toArray(Integer.valueOf(42), Double.valueOf(Math.PI))}, * there is no real advantage when compared to * {@code new Number[] {Integer.valueOf(42), Double.valueOf(Math.PI)}}. + *

    * - * @param the array's element type - * @param items the varargs array items, null allowed - * @return the array, not null unless a null array is passed in - * @since 3.0 + * @param the array's element type. + * @param items the varargs array items, null allowed. + * @return The array, not null unless a null array is passed in. + * @since 3.0 */ public static T[] toArray(@SuppressWarnings("unchecked") final T... items) { return items; } - // To map - //----------------------------------------------------------------------- /** - *

    Converts the given array into a {@link java.util.Map}. Each element of the array - * must be either a {@link java.util.Map.Entry} or an Array, containing at least two - * elements, where the first element is used as key and the second as - * value. + * Converts the given array into a {@link java.util.Map}. Each element of the array must be either a {@link java.util.Map.Entry} or an Array, containing at + * least two elements, where the first element is used as key and the second as value. + *

    + * This method can be used to initialize: + *

    * - *

    This method can be used to initialize: *

    +     *
          * // Create a Map mapping colors.
    -     * Map colorMap = ArrayUtils.toMap(new String[][] {
    -     *     {"RED", "#FF0000"},
    -     *     {"GREEN", "#00FF00"},
    -     *     {"BLUE", "#0000FF"}});
    +     * Map colorMap = ArrayUtils.toMap(new String[][] { { "RED", "#FF0000" }, { "GREEN", "#00FF00" }, { "BLUE", "#0000FF" } });
          * 
    + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - *

    This method returns {@code null} for a {@code null} input array. - * - * @param array an array whose elements are either a {@link java.util.Map.Entry} or - * an Array containing at least two elements, may be {@code null} - * @return a {@code Map} that was created from the array - * @throws IllegalArgumentException if one element of this Array is - * itself an Array containing less then two elements - * @throws IllegalArgumentException if the array contains elements other - * than {@link java.util.Map.Entry} and an Array + * @param array An array whose elements are either a {@link java.util.Map.Entry} or an Array containing at least two elements, may be {@code null}. + * @return A {@link Map} that was created from the array. + * @throws IllegalArgumentException if one element of this Array is itself an Array containing less than two elements. + * @throws IllegalArgumentException if the array contains elements other than {@link java.util.Map.Entry} and an Array. */ public static Map toMap(final Object[] array) { if (array == null) { @@ -8732,235 +9052,205 @@ public static Map toMap(final Object[] array) { } /** - *

    Converts an array of primitive booleans to objects. + * Converts an array of primitive booleans to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code boolean} array - * @return a {@code Boolean} array, {@code null} if null array input + * @param array A {@code boolean} array. + * @return A {@link Boolean} array, {@code null} if null array input. */ public static Boolean[] toObject(final boolean[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_BOOLEAN_OBJECT_ARRAY; } - final Boolean[] result = new Boolean[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = (array[i] ? Boolean.TRUE : Boolean.FALSE); + if (array.length == 0) { + return EMPTY_BOOLEAN_OBJECT_ARRAY; } - return result; + return setAll(new Boolean[array.length], i -> array[i] ? Boolean.TRUE : Boolean.FALSE); } /** - *

    Converts an array of primitive bytes to objects. + * Converts an array of primitive bytes to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code byte} array - * @return a {@code Byte} array, {@code null} if null array input + * @param array A {@code byte} array. + * @return A {@link Byte} array, {@code null} if null array input. */ public static Byte[] toObject(final byte[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_BYTE_OBJECT_ARRAY; } - final Byte[] result = new Byte[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Byte.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_BYTE_OBJECT_ARRAY; } - return result; + return setAll(new Byte[array.length], i -> Byte.valueOf(array[i])); } /** - *

    Converts an array of primitive chars to objects. + * Converts an array of primitive chars to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code char} array - * @return a {@code Character} array, {@code null} if null array input + * @param array A {@code char} array. + * @return A {@link Character} array, {@code null} if null array input. */ public static Character[] toObject(final char[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_CHARACTER_OBJECT_ARRAY; } - final Character[] result = new Character[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Character.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; } - return result; + return setAll(new Character[array.length], i -> Character.valueOf(array[i])); } /** - *

    Converts an array of primitive doubles to objects. + * Converts an array of primitive doubles to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code double} array - * @return a {@code Double} array, {@code null} if null array input + * @param array A {@code double} array. + * @return A {@link Double} array, {@code null} if null array input. */ public static Double[] toObject(final double[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_DOUBLE_OBJECT_ARRAY; } - final Double[] result = new Double[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Double.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_DOUBLE_OBJECT_ARRAY; } - return result; + return setAll(new Double[array.length], i -> Double.valueOf(array[i])); } /** - *

    Converts an array of primitive floats to objects. + * Converts an array of primitive floats to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code float} array - * @return a {@code Float} array, {@code null} if null array input + * @param array A {@code float} array. + * @return A {@link Float} array, {@code null} if null array input. */ public static Float[] toObject(final float[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_FLOAT_OBJECT_ARRAY; } - final Float[] result = new Float[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Float.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_FLOAT_OBJECT_ARRAY; } - return result; + return setAll(new Float[array.length], i -> Float.valueOf(array[i])); } /** - *

    Converts an array of primitive ints to objects. + * Converts an array of primitive ints to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array an {@code int} array - * @return an {@code Integer} array, {@code null} if null array input + * @param array An {@code int} array. + * @return An {@link Integer} array, {@code null} if null array input. */ public static Integer[] toObject(final int[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_INTEGER_OBJECT_ARRAY; } - final Integer[] result = new Integer[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Integer.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_INTEGER_OBJECT_ARRAY; } - return result; + return setAll(new Integer[array.length], i -> Integer.valueOf(array[i])); } /** - *

    Converts an array of primitive longs to objects. + * Converts an array of primitive longs to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code long} array - * @return a {@code Long} array, {@code null} if null array input + * @param array A {@code long} array. + * @return A {@link Long} array, {@code null} if null array input. */ public static Long[] toObject(final long[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_LONG_OBJECT_ARRAY; } - final Long[] result = new Long[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Long.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_LONG_OBJECT_ARRAY; } - return result; + return setAll(new Long[array.length], i -> Long.valueOf(array[i])); } /** - *

    Converts an array of primitive shorts to objects. + * Converts an array of primitive shorts to objects. * - *

    This method returns {@code null} for a {@code null} input array. + *

    This method returns {@code null} for a {@code null} input array.

    * - * @param array a {@code short} array - * @return a {@code Short} array, {@code null} if null array input + * @param array A {@code short} array. + * @return A {@link Short} array, {@code null} if null array input. */ public static Short[] toObject(final short[] array) { if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_SHORT_OBJECT_ARRAY; } - final Short[] result = new Short[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = Short.valueOf(array[i]); + if (array.length == 0) { + return EMPTY_SHORT_OBJECT_ARRAY; } - return result; + return setAll(new Short[array.length], i -> Short.valueOf(array[i])); } - // Boolean array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Booleans to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Booleans to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    + *

    + * Null array elements map to false, like {@code Boolean.parseBoolean(null)} and its callers return false. + *

    * - * @param array a {@code Boolean} array, may be {@code null} - * @return a {@code boolean} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Boolean} array, may be {@code null}. + * @return A {@code boolean} array, {@code null} if null array input. */ public static boolean[] toPrimitive(final Boolean[] array) { - if (array == null) { - return null; - } else if (array.length == 0) { - return EMPTY_BOOLEAN_ARRAY; - } - final boolean[] result = new boolean[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = array[i].booleanValue(); - } - return result; + return toPrimitive(array, false); } /** - *

    Converts an array of object Booleans to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Booleans to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Boolean} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code boolean} array, {@code null} if null array input + * @param array A {@link Boolean} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code boolean} array, {@code null} if null array input. */ public static boolean[] toPrimitive(final Boolean[] array, final boolean valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_BOOLEAN_ARRAY; } final boolean[] result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { final Boolean b = array[i]; - result[i] = (b == null ? valueForNull : b.booleanValue()); + result[i] = b == null ? valueForNull : b.booleanValue(); } return result; } - // Byte array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Bytes to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Bytes to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Byte} array, may be {@code null} - * @return a {@code byte} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Byte} array, may be {@code null}. + * @return A {@code byte} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static byte[] toPrimitive(final Byte[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_BYTE_ARRAY; } final byte[] result = new byte[array.length]; @@ -8971,43 +9261,45 @@ public static byte[] toPrimitive(final Byte[] array) { } /** - *

    Converts an array of object Bytes to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Bytes to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Byte} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code byte} array, {@code null} if null array input + * @param array A {@link Byte} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code byte} array, {@code null} if null array input. */ public static byte[] toPrimitive(final Byte[] array, final byte valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_BYTE_ARRAY; } final byte[] result = new byte[array.length]; for (int i = 0; i < array.length; i++) { final Byte b = array[i]; - result[i] = (b == null ? valueForNull : b.byteValue()); + result[i] = b == null ? valueForNull : b.byteValue(); } return result; } - // Character array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Characters to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Characters to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Character} array, may be {@code null} - * @return a {@code char} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Character} array, may be {@code null}. + * @return A {@code char} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static char[] toPrimitive(final Character[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_CHAR_ARRAY; } final char[] result = new char[array.length]; @@ -9018,43 +9310,45 @@ public static char[] toPrimitive(final Character[] array) { } /** - *

    Converts an array of object Character to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Character to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Character} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code char} array, {@code null} if null array input + * @param array A {@link Character} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code char} array, {@code null} if null array input. */ public static char[] toPrimitive(final Character[] array, final char valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_CHAR_ARRAY; } final char[] result = new char[array.length]; for (int i = 0; i < array.length; i++) { final Character b = array[i]; - result[i] = (b == null ? valueForNull : b.charValue()); + result[i] = b == null ? valueForNull : b.charValue(); } return result; } - // Double array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Doubles to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Doubles to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Double} array, may be {@code null} - * @return a {@code double} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Double} array, may be {@code null}. + * @return A {@code double} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static double[] toPrimitive(final Double[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_DOUBLE_ARRAY; } final double[] result = new double[array.length]; @@ -9065,43 +9359,45 @@ public static double[] toPrimitive(final Double[] array) { } /** - *

    Converts an array of object Doubles to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Doubles to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Double} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code double} array, {@code null} if null array input + * @param array A {@link Double} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code double} array, {@code null} if null array input. */ public static double[] toPrimitive(final Double[] array, final double valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_DOUBLE_ARRAY; } final double[] result = new double[array.length]; for (int i = 0; i < array.length; i++) { final Double b = array[i]; - result[i] = (b == null ? valueForNull : b.doubleValue()); + result[i] = b == null ? valueForNull : b.doubleValue(); } return result; } - // Float array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Floats to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Floats to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Float} array, may be {@code null} - * @return a {@code float} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Float} array, may be {@code null}. + * @return A {@code float} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static float[] toPrimitive(final Float[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_FLOAT_ARRAY; } final float[] result = new float[array.length]; @@ -9112,43 +9408,45 @@ public static float[] toPrimitive(final Float[] array) { } /** - *

    Converts an array of object Floats to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Floats to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Float} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code float} array, {@code null} if null array input + * @param array A {@link Float} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code float} array, {@code null} if null array input. */ public static float[] toPrimitive(final Float[] array, final float valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_FLOAT_ARRAY; } final float[] result = new float[array.length]; for (int i = 0; i < array.length; i++) { final Float b = array[i]; - result[i] = (b == null ? valueForNull : b.floatValue()); + result[i] = b == null ? valueForNull : b.floatValue(); } return result; } - // Int array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Integers to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Integers to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Integer} array, may be {@code null} - * @return an {@code int} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Integer} array, may be {@code null}. + * @return An {@code int} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static int[] toPrimitive(final Integer[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_INT_ARRAY; } final int[] result = new int[array.length]; @@ -9159,43 +9457,45 @@ public static int[] toPrimitive(final Integer[] array) { } /** - *

    Converts an array of object Integer to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Integer to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Integer} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return an {@code int} array, {@code null} if null array input + * @param array A {@link Integer} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return An {@code int} array, {@code null} if null array input. */ public static int[] toPrimitive(final Integer[] array, final int valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_INT_ARRAY; } final int[] result = new int[array.length]; for (int i = 0; i < array.length; i++) { final Integer b = array[i]; - result[i] = (b == null ? valueForNull : b.intValue()); + result[i] = b == null ? valueForNull : b.intValue(); } return result; } - // Long array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Longs to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Longs to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Long} array, may be {@code null} - * @return a {@code long} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Long} array, may be {@code null}. + * @return A {@code long} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static long[] toPrimitive(final Long[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_LONG_ARRAY; } final long[] result = new long[array.length]; @@ -9206,35 +9506,38 @@ public static long[] toPrimitive(final Long[] array) { } /** - *

    Converts an array of object Long to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Long to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Long} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code long} array, {@code null} if null array input + * @param array A {@link Long} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code long} array, {@code null} if null array input. */ public static long[] toPrimitive(final Long[] array, final long valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_LONG_ARRAY; } final long[] result = new long[array.length]; for (int i = 0; i < array.length; i++) { final Long b = array[i]; - result[i] = (b == null ? valueForNull : b.longValue()); + result[i] = b == null ? valueForNull : b.longValue(); } return result; } /** - *

    Create an array of primitive type from an array of wrapper types. - * - *

    This method returns {@code null} for a {@code null} input array. + * Create an array of primitive type from an array of wrapper types. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array an array of wrapper object - * @return an array of the corresponding primitive type, or the original array + * @param array An array of wrapper object. + * @return An array of the corresponding primitive type, or the original array. * @since 3.5 */ public static Object toPrimitive(final Object array) { @@ -9243,6 +9546,15 @@ public static Object toPrimitive(final Object array) { } final Class ct = array.getClass().getComponentType(); final Class pt = ClassUtils.wrapperToPrimitive(ct); + if (Boolean.TYPE.equals(pt)) { + return toPrimitive((Boolean[]) array); + } + if (Character.TYPE.equals(pt)) { + return toPrimitive((Character[]) array); + } + if (Byte.TYPE.equals(pt)) { + return toPrimitive((Byte[]) array); + } if (Integer.TYPE.equals(pt)) { return toPrimitive((Integer[]) array); } @@ -9261,21 +9573,21 @@ public static Object toPrimitive(final Object array) { return array; } - // Short array converters - // ---------------------------------------------------------------------- /** - *

    Converts an array of object Shorts to primitives. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Shorts to primitives. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Short} array, may be {@code null} - * @return a {@code byte} array, {@code null} if null array input - * @throws NullPointerException if array content is {@code null} + * @param array A {@link Short} array, may be {@code null}. + * @return A {@code byte} array, {@code null} if null array input. + * @throws NullPointerException if an array element is {@code null}. */ public static short[] toPrimitive(final Short[] array) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_SHORT_ARRAY; } final short[] result = new short[array.length]; @@ -9286,125 +9598,110 @@ public static short[] toPrimitive(final Short[] array) { } /** - *

    Converts an array of object Short to primitives handling {@code null}. - * - *

    This method returns {@code null} for a {@code null} input array. + * Converts an array of object Short to primitives handling {@code null}. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array a {@code Short} array, may be {@code null} - * @param valueForNull the value to insert if {@code null} found - * @return a {@code byte} array, {@code null} if null array input + * @param array A {@link Short} array, may be {@code null}. + * @param valueForNull The value to insert if {@code null} found. + * @return A {@code byte} array, {@code null} if null array input. */ public static short[] toPrimitive(final Short[] array, final short valueForNull) { if (array == null) { return null; - } else if (array.length == 0) { + } + if (array.length == 0) { return EMPTY_SHORT_ARRAY; } final short[] result = new short[array.length]; for (int i = 0; i < array.length; i++) { final Short b = array[i]; - result[i] = (b == null ? valueForNull : b.shortValue()); + result[i] = b == null ? valueForNull : b.shortValue(); } return result; } - // Basic methods handling multi-dimensional arrays - //----------------------------------------------------------------------- /** - *

    Outputs an array as a String, treating {@code null} as an empty array. - * - *

    Multi-dimensional arrays are handled correctly, including + * Outputs an array as a String, treating {@code null} as an empty array. + *

    + * Multi-dimensional arrays are handled correctly, including * multi-dimensional primitive arrays. + *

    + *

    + * The format is that of Java source code, for example {@code {a,b}}. + *

    * - *

    The format is that of Java source code, for example {@code {a,b}}. - * - * @param array the array to get a toString for, may be {@code null} - * @return a String representation of the array, '{}' if null array input + * @param array The array to get a toString for, may be {@code null}. + * @return A String representation of the array, '{}' if null array input. */ public static String toString(final Object array) { return toString(array, "{}"); } /** - *

    Outputs an array as a String handling {@code null}s. - * - *

    Multi-dimensional arrays are handled correctly, including + * Outputs an array as a String handling {@code null}s. + *

    + * Multi-dimensional arrays are handled correctly, including * multi-dimensional primitive arrays. + *

    + *

    + * The format is that of Java source code, for example {@code {a,b}}. + *

    * - *

    The format is that of Java source code, for example {@code {a,b}}. - * - * @param array the array to get a toString for, may be {@code null} - * @param stringIfNull the String to return if the array is {@code null} - * @return a String representation of the array + * @param array The array to get a toString for, may be {@code null}. + * @param stringIfNull The String to return if the array is {@code null}. + * @return A String representation of the array. */ public static String toString(final Object array, final String stringIfNull) { - if (array == null) { - return stringIfNull; - } - return new ToStringBuilder(array, ToStringStyle.SIMPLE_STYLE).append(array).toString(); + return array != null ? new ToStringBuilder(array, ToStringStyle.SIMPLE_STYLE).append(array).toString() : stringIfNull; } /** - *

    Returns an array containing the string representation of each element in the argument array.

    - * - *

    This method returns {@code null} for a {@code null} input array.

    + * Returns an array containing the string representation of each element in the argument array. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array the {@code Object[]} to be processed, may be null - * @return {@code String[]} of the same size as the source with its element's string representation, - * {@code null} if null array input - * @throws NullPointerException if array contains {@code null} + * @param array The {@code Object[]} to be processed, may be {@code null}. + * @return {@code String[]} of the same size as the source with its element's string representation, {@code null} if null array input. * @since 3.6 */ public static String[] toStringArray(final Object[] array) { - if (array == null) { - return null; - } else if (array.length == 0) { - return EMPTY_STRING_ARRAY; - } - - final String[] result = new String[array.length]; - for (int i = 0; i < array.length; i++) { - result[i] = array[i].toString(); - } - - return result; + return toStringArray(array, "null"); } /** - *

    Returns an array containing the string representation of each element in the argument - * array handling {@code null} elements.

    - * - *

    This method returns {@code null} for a {@code null} input array.

    + * Returns an array containing the string representation of each element in the argument array handling {@code null} elements. + *

    + * This method returns {@code null} for a {@code null} input array. + *

    * - * @param array the Object[] to be processed, may be null - * @param valueForNullElements the value to insert if {@code null} is found - * @return a {@code String} array, {@code null} if null array input + * @param array The Object[] to be processed, may be {@code null}. + * @param valueForNullElements The value to insert if {@code null} is found. + * @return A {@link String} array, {@code null} if null array input. * @since 3.6 */ public static String[] toStringArray(final Object[] array, final String valueForNullElements) { - if (null == array) { + if (array == null) { return null; - } else if (array.length == 0) { - return EMPTY_STRING_ARRAY; } - - final String[] result = new String[array.length]; - for (int i = 0; i < array.length; i++) { - final Object object = array[i]; - result[i] = (object == null ? valueForNullElements : object.toString()); + if (array.length == 0) { + return EMPTY_STRING_ARRAY; } - - return result; + return map(array, String.class, e -> Objects.toString(e, valueForNullElements)); } /** - *

    ArrayUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code ArrayUtils.clone(new int[] {2})}. + * ArrayUtils instances should NOT be constructed in standard programming. Instead, the class should be used as {@code ArrayUtils.clone(new int[] {2})}. + *

    + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

    * - *

    This constructor is public to permit tools that require a JavaBean instance - * to operate. + * @deprecated TODO Make private in 4.0. */ + @Deprecated public ArrayUtils() { - super(); + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/AutoCloseables.java b/src/main/java/org/apache/commons/lang3/AutoCloseables.java new file mode 100644 index 00000000000..a62495fbad6 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/AutoCloseables.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.io.Closeable; +import java.util.function.Consumer; + +import org.apache.commons.lang3.function.Consumers; +import org.apache.commons.lang3.function.FailableConsumer; + +/** + * Static operations on {@link AutoCloseable}. + *

    + * For {@link Closeable}-specific methods, see Apache Commons IO's + * IOUtils. + *

    + * + * @since 3.21.0 + */ +public class AutoCloseables { + + /** + * Closes the given {@link AutoCloseable} as a null-safe operation. + * + * @param closeable The resource to close, may be null. + * @throws Exception Thrown if an error occurs. + */ + public static void close(final AutoCloseable closeable) throws Exception { + if (closeable != null) { + closeable.close(); + } + } + + /** + * Closes the given {@link AutoCloseable} as a null-safe operation. + * + * @param closeable The resource to close, may be null. + * @param consumer Consume the Exception thrown by {@link AutoCloseable#close()}. + * @throws Exception As thrown by the consumer. + */ + public static void close(final AutoCloseable closeable, final FailableConsumer consumer) throws Exception { + if (closeable != null) { + try { + closeable.close(); + } catch (final Exception e) { + FailableConsumer.accept(consumer, e); + } + } + } + + /** + * Closes an {@link AutoCloseable}, never throwing an {@link Exception}. + *

    + * Equivalent to {@link AutoCloseable#close()}, except any exceptions will be ignored. + *

    + * + * @param closeable The objects to close, may be null or already closed. + * @see Throwable#addSuppressed(Throwable) + */ + public static void closeQuietly(final AutoCloseable closeable) { + closeQuietly(closeable, (Consumer) null); + } + + /** + * Closes the given {@link AutoCloseable} as a null-safe operation while consuming Exception by the given {@code consumer}. + * + * @param closeable The resource to close, may be null. + * @param consumer Consumes the Exception thrown by {@link AutoCloseable#close()}. + */ + public static void closeQuietly(final AutoCloseable closeable, final Consumer consumer) { + if (closeable != null) { + try { + closeable.close(); + } catch (final Exception e) { + Consumers.accept(consumer, e); + } + } + } + + /** + * Closes an iterable of {@link AutoCloseable}, never throwing an {@link Exception}. + *

    + * Equivalent calling {@link AutoCloseable#close()} on each element, except any exceptions will be ignored. + *

    + * + * @param closeables The objects to close, may be null or already closed. + * @see #closeQuietly(AutoCloseable) + */ + public static void closeQuietly(final Iterable closeables) { + if (closeables != null) { + closeables.forEach(AutoCloseables::closeQuietly); + } + } + + /** + * Closes a {@link Closeable} unconditionally and adds any exception thrown by the {@code close()} to the given Throwable. + *

    + * For example: + *

    + * + *
    +     * AutoCloseable autoCloseable = ...;
    +     * try {
    +     *     // process autoCloseable.
    +     * } catch (Exception e) {
    +     *     // Handle exception.
    +     *     throw AutoCloseables.closeQuietlySuppress(autoCloseable, e);
    +     * }
    +     * 
    + *

    + * Also consider using a try-with-resources statement where appropriate. + *

    + * + * @param The Throwable type. + * @param closeable The object to close, may be null or already closed. + * @param throwable Add the exception throw by the closeable to the given Throwable. + * @return The given Throwable. + * @see Throwable#addSuppressed(Throwable) + */ + public static T closeQuietlySuppress(final Closeable closeable, final T throwable) { + closeQuietly(closeable, throwable::addSuppressed); + return throwable; + } + + /** + * No instances needed. + */ + private AutoCloseables() { + // empty + } +} diff --git a/src/main/java/org/apache/commons/lang3/BitField.java b/src/main/java/org/apache/commons/lang3/BitField.java index dc70df4a5d8..28adb9ae9c8 100644 --- a/src/main/java/org/apache/commons/lang3/BitField.java +++ b/src/main/java/org/apache/commons/lang3/BitField.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,309 +14,425 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.commons.lang3; /** - *

    Supports operations on bit-mapped fields. Instances of this class can be - * used to store a flag or data within an {@code int}, {@code short} or - * {@code byte}.

    - * - *

    Each {@code BitField} is constructed with a mask value, which indicates - * the bits that will be used to store and retrieve the data for that field. - * For instance, the mask {@code 0xFF} indicates the least-significant byte - * should be used to store the data.

    - * - *

    As an example, consider a car painting machine that accepts - * paint instructions as integers. Bit fields can be used to encode this:

    - * - *
    - *    // blue, green and red are 1 byte values (0-255) stored in the three least
    - *    // significant bytes
    - *    BitField blue = new BitField(0xFF);
    - *    BitField green = new BitField(0xFF00);
    - *    BitField red = new BitField(0xFF0000);
    + * Supports operations on bit-mapped fields. Instances of this class can be used to store a flag or data within an {@code int}, {@code short} or {@code byte}.
    + * 

    + * Each {@link BitField} is constructed with a mask value, which indicates the bits that will be used to store and retrieve the data for that field. For + * instance, the mask {@code 0xFF} indicates the least-significant byte should be used to store the data. + *

    + *

    + * As an example, consider a car painting machine that accepts paint instructions as integers. Bit fields can be used to encode this: + *

    * - * // anyColor is a flag triggered if any color is used - * BitField anyColor = new BitField(0xFFFFFF); + *
      *
    - *    // isMetallic is a single bit flag
    - *    BitField isMetallic = new BitField(0x1000000);
    - *
    + * // blue, green and red are 1 byte values (0-255) stored in the three least + * // significant bytes + * BitField blue = new BitField(0xFF); * - *

    Using these {@code BitField} instances, a paint instruction can be - * encoded into an integer:

    + * BitField green = new BitField(0xFF00); * - *
    - *    int paintInstruction = 0;
    - *    paintInstruction = red.setValue(paintInstruction, 35);
    - *    paintInstruction = green.setValue(paintInstruction, 100);
    - *    paintInstruction = blue.setValue(paintInstruction, 255);
    - *
    + * BitField red = new BitField(0xFF0000); * - *

    Flags and data can be retrieved from the integer:

    + * // anyColor is a flag triggered if any color is used + * BitField anyColor = new BitField(0xFFFFFF); * - *
    - *    // Prints true if red, green or blue is non-zero
    - *    System.out.println(anyColor.isSet(paintInstruction));   // prints true
    + * // isMetallic is a single bit flag
    + * BitField isMetallic = new BitField(0x1000000);
    + * 
    + *

    + * Using these {@link BitField} instances, a paint instruction can be encoded into an integer: + *

    * - * // Prints value of red, green and blue - * System.out.println(red.getValue(paintInstruction)); // prints 35 - * System.out.println(green.getValue(paintInstruction)); // prints 100 - * System.out.println(blue.getValue(paintInstruction)); // prints 255 + *
    + * int paintInstruction = 0;
    + * paintInstruction = red.setValue(paintInstruction, 35);
    + * paintInstruction = green.setValue(paintInstruction, 100);
    + * paintInstruction = blue.setValue(paintInstruction, 255);
    + * 
    + *

    + * Flags and data can be retrieved from the integer: + *

    * - * // Prints true if isMetallic was set - * System.out.println(isMetallic.isSet(paintInstruction)); // prints false - *
    + *
    + * // Prints true if red, green or blue is non-zero
    + * System.out.println(anyColor.isSet(paintInstruction)); // prints true
    + * // Prints value of red, green and blue
    + * System.out.println(red.getValue(paintInstruction)); // prints 35
    + * System.out.println(green.getValue(paintInstruction)); // prints 100
    + * System.out.println(blue.getValue(paintInstruction)); // prints 255
    + * // Prints true if isMetallic was set
    + * System.out.println(isMetallic.isSet(paintInstruction)); // prints false
    + * 
    * * @since 2.0 */ public class BitField { - private final int _mask; - private final int _shift_count; + private final long mask; + + private final int shiftCount; /** - *

    Creates a BitField instance.

    + * Creates a BitField instance. * - * @param mask the mask specifying which bits apply to this - * BitField. Bits that are set in this mask are the bits - * that this BitField operates on + * @param mask The mask specifying which bits apply to this BitField. Bits that are set in this mask are the bits that this BitField operates on. */ public BitField(final int mask) { - _mask = mask; - _shift_count = mask == 0 ? 0 : Integer.numberOfTrailingZeros(mask); + this.mask = Integer.toUnsignedLong(mask); + this.shiftCount = this.mask == 0 ? 0 : Long.numberOfTrailingZeros(this.mask); } /** - *

    Obtains the value for the specified BitField, appropriately - * shifted right.

    + * Creates a BitField instance. + *

    + * If any bit above bit 31 is set in the mask, the resulting field can only be used with the {@code long} holder accessors; the {@code int}, {@code short} + * and {@code byte} holder accessors throw {@link IllegalStateException} for such a field, because those holder types cannot contain the masked bits and + * would otherwise silently answer wrongly (shift counts are truncated mod 32 and negative holders are sign-extended into bits 32-63). + *

    * - *

    Many users of a BitField will want to treat the specified - * bits as an int value, and will not want to be aware that the - * value is stored as a BitField (and so shifted left so many - * bits).

    + * @param mask The mask specifying which bits apply to this BitField. Bits that are set in this mask are the bits that this BitField operates on. + * @since 3.21.0 + */ + public BitField(final long mask) { + this.mask = mask; + this.shiftCount = mask == 0 ? 0 : Long.numberOfTrailingZeros(mask); + } + + /** + * Clears the bits. * - * @see #setValue(int,int) - * @param holder the int data containing the bits we're interested - * in - * @return the selected bits, shifted right appropriately + * @param holder The int data containing the bits we're interested in. + * @return The value of holder with the specified bits cleared (set to {@code 0}). + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public int getValue(final int holder) { - return getRawValue(holder) >> _shift_count; + public int clear(final int holder) { + return (int) (holder & ~intMask()); } /** - *

    Obtains the value for the specified BitField, appropriately - * shifted right, as a short.

    + * Clears the bits. * - *

    Many users of a BitField will want to treat the specified - * bits as an int value, and will not want to be aware that the - * value is stored as a BitField (and so shifted left so many - * bits).

    + * @param holder The long data containing the bits we're interested in. + * @return The value of holder with the specified bits cleared (set to {@code 0}). + * @since 3.21.0 + */ + public long clear(final long holder) { + return holder & ~mask; + } + + /** + * Clears the bits. * - * @see #setShortValue(short,short) - * @param holder the short data containing the bits we're - * interested in - * @return the selected bits, shifted right appropriately + * @param holder The byte data containing the bits we're interested in. + * @return The value of holder with the specified bits cleared (set to {@code 0}). + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public short getShortValue(final short holder) { - return (short) getValue(holder); + public byte clearByte(final byte holder) { + return (byte) clear(holder); + } + + /** + * Clears the bits. + * + * @param holder The short data containing the bits we're interested in. + * @return The value of holder with the specified bits cleared (set to {@code 0}). + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. + */ + public short clearShort(final short holder) { + return (short) clear(holder); } /** - *

    Obtains the value for the specified BitField, unshifted.

    + * Gets the value for the specified BitField, unshifted. * - * @param holder the int data containing the bits we're - * interested in - * @return the selected bits + * @param holder The int data containing the bits we're interested in. + * @return The selected bits. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ public int getRawValue(final int holder) { - return holder & _mask; + return (int) (holder & intMask()); } /** - *

    Obtains the value for the specified BitField, unshifted.

    + * Gets the value for the specified BitField, unshifted. * - * @param holder the short data containing the bits we're - * interested in - * @return the selected bits + * @param holder The long data containing the bits we're interested in. + * @return The selected bits. + * @since 3.21.0 + */ + public long getRawValue(final long holder) { + return holder & mask; + } + + /** + * Obtains the value for the specified BitField, unshifted. + * + * @param holder The short data containing the bits we're interested in. + * @return The selected bits. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ public short getShortRawValue(final short holder) { return (short) getRawValue(holder); } /** - *

    Returns whether the field is set or not.

    - * - *

    This is most commonly used for a single-bit field, which is - * often used to represent a boolean value; the results of using - * it for a multi-bit field is to determine whether *any* of its - * bits are set.

    + * Gets the value for the specified BitField, appropriately shifted right, as a short. + *

    + * Many users of a BitField will want to treat the specified bits as an int value, and will not want to be aware that the value is stored as a BitField (and + * so shifted left so many bits). + *

    * - * @param holder the int data containing the bits we're interested - * in - * @return {@code true} if any of the bits are set, - * else {@code false} + * @param holder The short data containing the bits we're interested in. + * @return The selected bits, shifted right appropriately. + * @see #setShortValue(short,short) + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public boolean isSet(final int holder) { - return (holder & _mask) != 0; + public short getShortValue(final short holder) { + return (short) getValue(holder); } /** - *

    Returns whether all of the bits are set or not.

    + * Gets the value for the specified BitField, appropriately shifted right. + *

    + * Many users of a BitField will want to treat the specified bits as an int value, and will not want to be aware that the value is stored as a BitField (and + * so shifted left so many bits). + *

    * - *

    This is a stricter test than {@link #isSet(int)}, - * in that all of the bits in a multi-bit set must be set - * for this method to return {@code true}.

    - * - * @param holder the int data containing the bits we're - * interested in - * @return {@code true} if all of the bits are set, - * else {@code false} + * @param holder The int data containing the bits we're interested in. + * @return The selected bits, shifted right appropriately. + * @see #setValue(int,int) + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public boolean isAllSet(final int holder) { - return (holder & _mask) == _mask; + public int getValue(final int holder) { + return getRawValue(holder) >>> shiftCount; } /** - *

    Replaces the bits with new values.

    + * Gets the value for the specified BitField, appropriately shifted right. + *

    + * Many users of a BitField will want to treat the specified bits as an long value, and will not want to be aware that the value is stored as a BitField (and + * so shifted left so many bits). + *

    * - * @see #getValue(int) - * @param holder the int data containing the bits we're - * interested in - * @param value the new value for the specified bits - * @return the value of holder with the bits from the value - * parameter replacing the old bits + * @param holder The long data containing the bits we're interested in. + * @return The selected bits, shifted right appropriately. + * @see #setValue(long,long) + * @since 3.21.0 */ - public int setValue(final int holder, final int value) { - return (holder & ~_mask) | ((value << _shift_count) & _mask); + public long getValue(final long holder) { + return getRawValue(holder) >>> shiftCount; } /** - *

    Replaces the bits with new values.

    + * Verifies that this field's mask fits in an {@code int} holder before an {@code int}, {@code short} or {@code byte} accessor uses it. + *

    + * Without this check, a mask with bits above bit 31 makes the narrow accessors silently wrong: the {@code int} shift count is truncated mod 32, and a + * negative narrow holder is sign-extended to 64 bits before the {@code long} mask is applied, reporting above-bit-31 flags as set even though the holder + * type cannot contain them. + *

    * - * @see #getShortValue(short) - * @param holder the short data containing the bits we're - * interested in - * @param value the new value for the specified bits - * @return the value of holder with the bits from the value - * parameter replacing the old bits + * @return the mask, guaranteed to fit in 32 bits. + * @throws IllegalStateException if the mask has bits set above bit 31. */ - public short setShortValue(final short holder, final short value) { - return (short) setValue(holder, value); + private long intMask() { + if (mask >>> Integer.SIZE != 0) { + throw new IllegalStateException("BitField mask 0x" + Long.toHexString(mask) + " exceeds 32 bits; use the long accessors for this field."); + } + return mask; } /** - *

    Clears the bits.

    + * Tests whether all of the bits are set or not. + *

    + * This is a stricter test than {@link #isSet(int)}, in that all of the bits in a multi-bit set must be set for this method to return {@code true}. + *

    * - * @param holder the int data containing the bits we're - * interested in - * @return the value of holder with the specified bits cleared - * (set to {@code 0}) + * @param holder The int data containing the bits we're interested in. + * @return {@code true} if all of the bits are set, else {@code false}. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public int clear(final int holder) { - return holder & ~_mask; + public boolean isAllSet(final int holder) { + final long intMask = intMask(); + return (holder & intMask) == intMask; } /** - *

    Clears the bits.

    + * Tests whether all of the bits are set or not. + *

    + * This is a stricter test than {@link #isSet(long)}, in that all of the bits in a multi-bit set must be set for this method to return {@code true}. + *

    * - * @param holder the short data containing the bits we're - * interested in - * @return the value of holder with the specified bits cleared - * (set to {@code 0}) + * @param holder The long data containing the bits we're interested in. + * @return {@code true} if all of the bits are set, else {@code false}. + * @since 3.21.0 */ - public short clearShort(final short holder) { - return (short) clear(holder); + public boolean isAllSet(final long holder) { + return (holder & mask) == mask; } /** - *

    Clears the bits.

    + * Tests whether the field is set or not. + *

    + * This is most commonly used for a single-bit field, which is often used to represent a boolean value; the results of using it for a multi-bit field is to + * determine whether any of its bits are set. + *

    * - * @param holder the byte data containing the bits we're - * interested in + * @param holder The int data containing the bits we're interested in + * @return {@code true} if any of the bits are set, else {@code false} + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. + */ + public boolean isSet(final int holder) { + return (holder & intMask()) != 0; + } + + /** + * Tests whether the field is set or not. + *

    + * This is most commonly used for a single-bit field, which is often used to represent a boolean value; the results of using it for a multi-bit field is to + * determine whether any of its bits are set. + *

    * - * @return the value of holder with the specified bits cleared - * (set to {@code 0}) + * @param holder The long data containing the bits we're interested in + * @return {@code true} if any of the bits are set, else {@code false} + * @since 3.21.0 */ - public byte clearByte(final byte holder) { - return (byte) clear(holder); + public boolean isSet(final long holder) { + return (holder & mask) != 0; } /** - *

    Sets the bits.

    + * Sets the bits. * - * @param holder the int data containing the bits we're - * interested in - * @return the value of holder with the specified bits set - * to {@code 1} + * @param holder The int data containing the bits we're interested in. + * @return The value of holder with the specified bits set to {@code 1}. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ public int set(final int holder) { - return holder | _mask; + return (int) (holder | intMask()); } /** - *

    Sets the bits.

    + * Sets the bits. * - * @param holder the short data containing the bits we're - * interested in - * @return the value of holder with the specified bits set - * to {@code 1} + * @param holder The long data containing the bits we're interested in. + * @return The value of holder with the specified bits set to {@code 1}. + * @since 3.21.0 */ - public short setShort(final short holder) { - return (short) set(holder); + public long set(final long holder) { + return holder | mask; } /** - *

    Sets the bits.

    + * Sets a boolean BitField. * - * @param holder the byte data containing the bits we're - * interested in + * @param holder The int data containing the bits we're interested in. + * @param flag indicating whether to set or clear the bits. + * @return The value of holder with the specified bits set or cleared. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. + */ + public int setBoolean(final int holder, final boolean flag) { + return flag ? set(holder) : clear(holder); + } + + /** + * Sets a boolean BitField. + * + * @param holder The long data containing the bits we're interested in. + * @param flag indicating whether to set or clear the bits. + * @return The value of holder with the specified bits set or cleared. + * @since 3.21.0 + */ + public long setBoolean(final long holder, final boolean flag) { + return flag ? set(holder) : clear(holder); + } + + /** + * Sets the bits. * - * @return the value of holder with the specified bits set - * to {@code 1} + * @param holder The byte data containing the bits we're interested in + * @return The value of holder with the specified bits set to {@code 1} + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ public byte setByte(final byte holder) { return (byte) set(holder); } /** - *

    Sets a boolean BitField.

    + * Sets a boolean BitField. * - * @param holder the int data containing the bits we're - * interested in - * @param flag indicating whether to set or clear the bits - * @return the value of holder with the specified bits set or - * cleared + * @param holder The byte data containing the bits we're interested in. + * @param flag indicating whether to set or clear the bits. + * @return The value of holder with the specified bits set or cleared. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public int setBoolean(final int holder, final boolean flag) { - return flag ? set(holder) : clear(holder); + public byte setByteBoolean(final byte holder, final boolean flag) { + return flag ? setByte(holder) : clearByte(holder); + } + + /** + * Sets the bits. + * + * @param holder The short data containing the bits we're interested in. + * @return The value of holder with the specified bits set to {@code 1}. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. + */ + public short setShort(final short holder) { + return (short) set(holder); } /** - *

    Sets a boolean BitField.

    + * Sets a boolean BitField. * - * @param holder the short data containing the bits we're - * interested in - * @param flag indicating whether to set or clear the bits - * @return the value of holder with the specified bits set or - * cleared + * @param holder The short data containing the bits we're interested in. + * @param flag indicating whether to set or clear the bits. + * @return The value of holder with the specified bits set or cleared. + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ public short setShortBoolean(final short holder, final boolean flag) { return flag ? setShort(holder) : clearShort(holder); } /** - *

    Sets a boolean BitField.

    + * Sets the bits with new values. * - * @param holder the byte data containing the bits we're - * interested in - * @param flag indicating whether to set or clear the bits - * @return the value of holder with the specified bits set or - * cleared + * @param holder The short data containing the bits we're interested in + * @param value The new value for the specified bits + * @return The value of holder with the bits from the value parameter replacing the old bits + * @see #getShortValue(short) + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. */ - public byte setByteBoolean(final byte holder, final boolean flag) { - return flag ? setByte(holder) : clearByte(holder); + public short setShortValue(final short holder, final short value) { + return (short) setValue(holder, value); + } + + /** + * Sets the bits with new values. + * + * @param holder The int data containing the bits we're interested in. + * @param value The new value for the specified bits. + * @return The value of holder with the bits from the value parameter replacing the old bits. + * @see #getValue(int) + * @throws IllegalStateException if this field's mask has bits set above bit 31 (only possible via {@link #BitField(long)}) and so cannot be represented in this holder type. + */ + public int setValue(final int holder, final int value) { + final long intMask = intMask(); + return (int) (holder & ~intMask | value << shiftCount & intMask); } + /** + * Sets the bits with new values. + * + * @param holder The long data containing the bits we're interested in. + * @param value The new value for the specified bits. + * @return The value of holder with the bits from the value parameter replacing the old bits. + * @see #getValue(long) + * @since 3.21.0 + */ + public long setValue(final long holder, final long value) { + return holder & ~mask | value << shiftCount & mask; + } } diff --git a/src/main/java/org/apache/commons/lang3/BooleanUtils.java b/src/main/java/org/apache/commons/lang3/BooleanUtils.java index 9dbcc65634b..d25fc524ff3 100644 --- a/src/main/java/org/apache/commons/lang3/BooleanUtils.java +++ b/src/main/java/org/apache/commons/lang3/BooleanUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,98 +16,165 @@ */ package org.apache.commons.lang3; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + import org.apache.commons.lang3.math.NumberUtils; /** - *

    Operations on boolean primitives and Boolean objects.

    + * Operations on boolean primitives and Boolean objects. * *

    This class tries to handle {@code null} input gracefully. * An exception will not be thrown for a {@code null} input. - * Each method documents its behaviour in more detail.

    + * Each method documents its behavior in more detail.

    * *

    #ThreadSafe#

    + * * @since 2.0 */ public class BooleanUtils { + private static final List BOOLEAN_LIST = Collections.unmodifiableList(Arrays.asList(Boolean.FALSE, Boolean.TRUE)); + /** - *

    {@code BooleanUtils} instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code BooleanUtils.negate(true);}.

    + * The false String {@code "false"}. * - *

    This constructor is public to permit tools that require a JavaBean instance - * to operate.

    + * @since 3.12.0 */ - public BooleanUtils() { - super(); - } + public static final String FALSE = "false"; - // Boolean utilities - //-------------------------------------------------------------------------- /** - *

    Negates the specified boolean.

    + * The no String {@code "no"}. * - *

    If {@code null} is passed in, {@code null} will be returned.

    + * @since 3.12.0 + */ + public static final String NO = "no"; + + /** + * The off String {@code "off"}. * - *

    NOTE: This returns {@code null} and will throw a {@code NullPointerException} - * if unboxed to a boolean.

    + * @since 3.12.0 + */ + public static final String OFF = "off"; + + /** + * The on String {@code "on"}. + * + * @since 3.12.0 + */ + public static final String ON = "on"; + + /** + * The true String {@code "true"}. + * + * @since 3.12.0 + */ + public static final String TRUE = "true"; + + /** + * The yes String {@code "yes"}. + * + * @since 3.12.0 + */ + public static final String YES = "yes"; + + /** + * Performs an 'and' operation on a set of booleans. * *
    -     *   BooleanUtils.negate(Boolean.TRUE)  = Boolean.FALSE;
    -     *   BooleanUtils.negate(Boolean.FALSE) = Boolean.TRUE;
    -     *   BooleanUtils.negate(null)          = null;
    +     *   BooleanUtils.and(true, true)         = true
    +     *   BooleanUtils.and(false, false)       = false
    +     *   BooleanUtils.and(true, false)        = false
    +     *   BooleanUtils.and(true, true, false)  = false
    +     *   BooleanUtils.and(true, true, true)   = true
          * 
    * - * @param bool the Boolean to negate, may be null - * @return the negated Boolean, or {@code null} if {@code null} input + * @param array An array of {@code boolean}s + * @return The result of the logical 'and' operation. That is {@code false} + * if any of the parameters is {@code false} and {@code true} otherwise. + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @since 3.0.1 */ - public static Boolean negate(final Boolean bool) { - if (bool == null) { - return null; + public static boolean and(final boolean... array) { + ObjectUtils.requireNonEmpty(array, "array"); + for (final boolean element : array) { + if (!element) { + return false; + } } - return bool.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + return true; } - // boolean Boolean methods - //----------------------------------------------------------------------- /** - *

    Checks if a {@code Boolean} value is {@code true}, - * handling {@code null} by returning {@code false}.

    - * + * Performs an 'and' operation on an array of Booleans. *
    -     *   BooleanUtils.isTrue(Boolean.TRUE)  = true
    -     *   BooleanUtils.isTrue(Boolean.FALSE) = false
    -     *   BooleanUtils.isTrue(null)          = false
    +     *   BooleanUtils.and(Boolean.TRUE, Boolean.TRUE)                 = Boolean.TRUE
    +     *   BooleanUtils.and(Boolean.FALSE, Boolean.FALSE)               = Boolean.FALSE
    +     *   BooleanUtils.and(Boolean.TRUE, Boolean.FALSE)                = Boolean.FALSE
    +     *   BooleanUtils.and(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)   = Boolean.TRUE
    +     *   BooleanUtils.and(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE) = Boolean.FALSE
    +     *   BooleanUtils.and(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE)  = Boolean.FALSE
    +     *   BooleanUtils.and(null, null)                                 = Boolean.FALSE
          * 
    + *

    + * Null array elements map to false, like {@code Boolean.parseBoolean(null)} and its callers return false. + *

    * - * @param bool the boolean to check, {@code null} returns {@code false} - * @return {@code true} only if the input is non-null and true - * @since 2.1 + * @param array An array of {@link Boolean}s + * @return The result of the logical 'and' operation. That is {@code false} + * if any of the parameters is {@code false} and {@code true} otherwise. + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @since 3.0.1 */ - public static boolean isTrue(final Boolean bool) { - return Boolean.TRUE.equals(bool); + public static Boolean and(final Boolean... array) { + ObjectUtils.requireNonEmpty(array, "array"); + return and(ArrayUtils.toPrimitive(array)) ? Boolean.TRUE : Boolean.FALSE; } /** - *

    Checks if a {@code Boolean} value is not {@code true}, - * handling {@code null} by returning {@code true}.

    + * Returns a new array of possible values (like an enum would). * - *
    -     *   BooleanUtils.isNotTrue(Boolean.TRUE)  = false
    -     *   BooleanUtils.isNotTrue(Boolean.FALSE) = true
    -     *   BooleanUtils.isNotTrue(null)          = true
    -     * 
    + * @return A new array of possible values (like an enum would). + * @since 3.12.0 + */ + public static Boolean[] booleanValues() { + return new Boolean[] {Boolean.FALSE, Boolean.TRUE}; + } + + /** + * Compares two {@code boolean} values. This is the same functionality as provided in Java 7. * - * @param bool the boolean to check, null returns {@code true} - * @return {@code true} if the input is null or false - * @since 2.3 + * @param x The first {@code boolean} to compare + * @param y The second {@code boolean} to compare + * @return The value {@code 0} if {@code x == y}; + * a value less than {@code 0} if {@code !x && y}; and + * a value greater than {@code 0} if {@code x && !y} + * @since 3.4 */ - public static boolean isNotTrue(final Boolean bool) { - return !isTrue(bool); + public static int compare(final boolean x, final boolean y) { + if (x == y) { + return 0; + } + return x ? 1 : -1; } /** - *

    Checks if a {@code Boolean} value is {@code false}, - * handling {@code null} by returning {@code false}.

    + * Performs the given action for each Boolean {@link BooleanUtils#values()}. + * + * @param action The action to be performed for each element + * @since 3.13.0 + */ + public static void forEach(final Consumer action) { + values().forEach(action); + } + + /** + * Checks if a {@link Boolean} value is {@code false}, + * handling {@code null} by returning {@code false}. * *
          *   BooleanUtils.isFalse(Boolean.TRUE)  = false
    @@ -115,7 +182,7 @@ public static boolean isNotTrue(final Boolean bool) {
          *   BooleanUtils.isFalse(null)          = false
          * 
    * - * @param bool the boolean to check, null returns {@code false} + * @param bool The boolean to check, null returns {@code false} * @return {@code true} only if the input is non-{@code null} and {@code false} * @since 2.1 */ @@ -124,8 +191,8 @@ public static boolean isFalse(final Boolean bool) { } /** - *

    Checks if a {@code Boolean} value is not {@code false}, - * handling {@code null} by returning {@code true}.

    + * Checks if a {@link Boolean} value is not {@code false}, + * handling {@code null} by returning {@code true}. * *
          *   BooleanUtils.isNotFalse(Boolean.TRUE)  = true
    @@ -133,7 +200,7 @@ public static boolean isFalse(final Boolean bool) {
          *   BooleanUtils.isNotFalse(null)          = true
          * 
    * - * @param bool the boolean to check, null returns {@code true} + * @param bool The boolean to check, null returns {@code true} * @return {@code true} if the input is {@code null} or {@code true} * @since 2.3 */ @@ -141,113 +208,218 @@ public static boolean isNotFalse(final Boolean bool) { return !isFalse(bool); } - //----------------------------------------------------------------------- /** - *

    Converts a Boolean to a boolean handling {@code null} - * by returning {@code false}.

    + * Checks if a {@link Boolean} value is not {@code true}, + * handling {@code null} by returning {@code true}. * *
    -     *   BooleanUtils.toBoolean(Boolean.TRUE)  = true
    -     *   BooleanUtils.toBoolean(Boolean.FALSE) = false
    -     *   BooleanUtils.toBoolean(null)          = false
    +     *   BooleanUtils.isNotTrue(Boolean.TRUE)  = false
    +     *   BooleanUtils.isNotTrue(Boolean.FALSE) = true
    +     *   BooleanUtils.isNotTrue(null)          = true
          * 
    * - * @param bool the boolean to convert - * @return {@code true} or {@code false}, {@code null} returns {@code false} + * @param bool The boolean to check, null returns {@code true} + * @return {@code true} if the input is null or false + * @since 2.3 */ - public static boolean toBoolean(final Boolean bool) { - return bool != null && bool.booleanValue(); + public static boolean isNotTrue(final Boolean bool) { + return !isTrue(bool); } /** - *

    Converts a Boolean to a boolean handling {@code null}.

    + * Checks if a {@link Boolean} value is {@code true}, + * handling {@code null} by returning {@code false}. * *
    -     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, false)  = true
    -     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, true)   = true
    -     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, true)  = false
    -     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, false) = false
    -     *   BooleanUtils.toBooleanDefaultIfNull(null, true)           = true
    -     *   BooleanUtils.toBooleanDefaultIfNull(null, false)          = false
    +     *   BooleanUtils.isTrue(Boolean.TRUE)  = true
    +     *   BooleanUtils.isTrue(Boolean.FALSE) = false
    +     *   BooleanUtils.isTrue(null)          = false
          * 
    * - * @param bool the boolean object to convert to primitive - * @param valueIfNull the boolean value to return if the parameter {@code bool} is {@code null} - * @return {@code true} or {@code false} + * @param bool The boolean to check, {@code null} returns {@code false} + * @return {@code true} only if the input is non-null and true + * @since 2.1 */ - public static boolean toBooleanDefaultIfNull(final Boolean bool, final boolean valueIfNull) { + public static boolean isTrue(final Boolean bool) { + return Boolean.TRUE.equals(bool); + } + + /** + * Negates the specified boolean. + * + *

    If {@code null} is passed in, {@code null} will be returned.

    + * + *

    NOTE: This returns {@code null} and will throw a {@link NullPointerException} + * if unboxed to a boolean.

    + * + *
    +     *   BooleanUtils.negate(Boolean.TRUE)  = Boolean.FALSE;
    +     *   BooleanUtils.negate(Boolean.FALSE) = Boolean.TRUE;
    +     *   BooleanUtils.negate(null)          = null;
    +     * 
    + * + * @param bool The Boolean to negate, may be null + * @return The negated Boolean, or {@code null} if {@code null} input + */ + public static Boolean negate(final Boolean bool) { if (bool == null) { - return valueIfNull; + return null; } - return bool.booleanValue(); + return bool.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + } + + /** + * Performs a one-hot on an array of booleans. + *

    + * This implementation returns true if one, and only one, of the supplied values is true. + *

    + *

    + * See also One-hot. + *

    + * + * @param array An array of {@code boolean}s + * @return The result of the one-hot operations + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + */ + public static boolean oneHot(final boolean... array) { + ObjectUtils.requireNonEmpty(array, "array"); + boolean result = false; + for (final boolean element: array) { + if (element) { + if (result) { + return false; + } + result = true; + } + } + return result; } - // Integer to Boolean methods - //----------------------------------------------------------------------- /** - *

    Converts an int to a boolean using the convention that {@code zero} - * is {@code false}, everything else is {@code true}.

    + * Performs a one-hot on an array of booleans. + *

    + * This implementation returns true if one, and only one, of the supplied values is true. + *

    + *

    + * Null array elements map to false, like {@code Boolean.parseBoolean(null)} and its callers return false. + *

    + *

    + * See also One-hot. + *

    + * + * @param array An array of {@code boolean}s + * @return The result of the one-hot operations + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + */ + public static Boolean oneHot(final Boolean... array) { + return Boolean.valueOf(oneHot(ArrayUtils.toPrimitive(array))); + } + + /** + * Performs an 'or' operation on a set of booleans. * *
    -     *   BooleanUtils.toBoolean(0) = false
    -     *   BooleanUtils.toBoolean(1) = true
    -     *   BooleanUtils.toBoolean(2) = true
    +     *   BooleanUtils.or(true, true)          = true
    +     *   BooleanUtils.or(false, false)        = false
    +     *   BooleanUtils.or(true, false)         = true
    +     *   BooleanUtils.or(true, true, false)   = true
    +     *   BooleanUtils.or(true, true, true)    = true
    +     *   BooleanUtils.or(false, false, false) = false
          * 
    * - * @param value the int to convert - * @return {@code true} if non-zero, {@code false} - * if zero + * @param array An array of {@code boolean}s + * @return {@code true} if any of the arguments is {@code true}, and it returns {@code false} otherwise. + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @since 3.0.1 */ - public static boolean toBoolean(final int value) { - return value != 0; + public static boolean or(final boolean... array) { + ObjectUtils.requireNonEmpty(array, "array"); + for (final boolean element : array) { + if (element) { + return true; + } + } + return false; } /** - *

    Converts an int to a Boolean using the convention that {@code zero} - * is {@code false}, everything else is {@code true}.

    - * + * Performs an 'or' operation on an array of Booleans. *
    -     *   BooleanUtils.toBoolean(0) = Boolean.FALSE
    -     *   BooleanUtils.toBoolean(1) = Boolean.TRUE
    -     *   BooleanUtils.toBoolean(2) = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.TRUE, Boolean.TRUE)                  = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE)                = Boolean.FALSE
    +     *   BooleanUtils.or(Boolean.TRUE, Boolean.FALSE)                 = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)    = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE)  = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE)   = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE, Boolean.FALSE) = Boolean.FALSE
    +     *   BooleanUtils.or(Boolean.TRUE, null)                          = Boolean.TRUE
    +     *   BooleanUtils.or(Boolean.FALSE, null)                         = Boolean.FALSE
          * 
    + *

    + * Null array elements map to false, like {@code Boolean.parseBoolean(null)} and its callers return false. + *

    * - * @param value the int to convert - * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, - * {@code null} if {@code null} + * @param array An array of {@link Boolean}s + * @return {@code true} if any of the arguments is {@code true}, and it returns {@code false} otherwise. + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @since 3.0.1 */ - public static Boolean toBooleanObject(final int value) { - return value == 0 ? Boolean.FALSE : Boolean.TRUE; + public static Boolean or(final Boolean... array) { + ObjectUtils.requireNonEmpty(array, "array"); + return or(ArrayUtils.toPrimitive(array)) ? Boolean.TRUE : Boolean.FALSE; } /** - *

    Converts an Integer to a Boolean using the convention that {@code zero} - * is {@code false}, every other numeric value is {@code true}.

    + * Returns a new array of possible values (like an enum would). * - *

    {@code null} will be converted to {@code null}.

    + * @return A new array of possible values (like an enum would). + * @since 3.12.0 + */ + public static boolean[] primitiveValues() { + return new boolean[] {false, true}; + } + + /** + * Converts a Boolean to a boolean handling {@code null} + * by returning {@code false}. * - *

    NOTE: This method may return {@code null} and may throw a {@code NullPointerException} - * if unboxed to a {@code boolean}.

    + *
    +     *   BooleanUtils.toBoolean(Boolean.TRUE)  = true
    +     *   BooleanUtils.toBoolean(Boolean.FALSE) = false
    +     *   BooleanUtils.toBoolean(null)          = false
    +     * 
    + * + * @param bool The boolean to convert + * @return {@code true} or {@code false}, {@code null} returns {@code false} + */ + public static boolean toBoolean(final Boolean bool) { + return bool != null && bool.booleanValue(); + } + + /** + * Converts an int to a boolean using the convention that {@code zero} + * is {@code false}, everything else is {@code true}. * *
    -     *   BooleanUtils.toBoolean(Integer.valueOf(0))    = Boolean.FALSE
    -     *   BooleanUtils.toBoolean(Integer.valueOf(1))    = Boolean.TRUE
    -     *   BooleanUtils.toBoolean(Integer.valueOf(null)) = null
    +     *   BooleanUtils.toBoolean(0) = false
    +     *   BooleanUtils.toBoolean(1) = true
    +     *   BooleanUtils.toBoolean(2) = true
          * 
    * - * @param value the Integer to convert - * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, - * {@code null} if {@code null} input + * @param value The int to convert + * @return {@code true} if non-zero, {@code false} + * if zero */ - public static Boolean toBooleanObject(final Integer value) { - if (value == null) { - return null; - } - return value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE; + public static boolean toBoolean(final int value) { + return value != 0; } /** - *

    Converts an int to a boolean specifying the conversion values.

    + * Converts an int to a boolean specifying the conversion values. * *

    If the {@code trueValue} and {@code falseValue} are the same number then * the return value will be {@code true} in case {@code value} matches it.

    @@ -260,9 +432,9 @@ public static Boolean toBooleanObject(final Integer value) { * BooleanUtils.toBoolean(2, 2, 0) = true * * - * @param value the {@code Integer} to convert - * @param trueValue the value to match for {@code true} - * @param falseValue the value to match for {@code false} + * @param value The {@link Integer} to convert + * @param trueValue The value to match for {@code true} + * @param falseValue The value to match for {@code false} * @return {@code true} or {@code false} * @throws IllegalArgumentException if {@code value} does not match neither * {@code trueValue} no {@code falseValue} @@ -278,7 +450,7 @@ public static boolean toBoolean(final int value, final int trueValue, final int } /** - *

    Converts an Integer to a boolean specifying the conversion values.

    + * Converts an Integer to a boolean specifying the conversion values. * *
          *   BooleanUtils.toBoolean(Integer.valueOf(0), Integer.valueOf(1), Integer.valueOf(0)) = false
    @@ -288,9 +460,9 @@ public static boolean toBoolean(final int value, final int trueValue, final int
          *   BooleanUtils.toBoolean(null, null, Integer.valueOf(0))                     = true
          * 
    * - * @param value the Integer to convert - * @param trueValue the value to match for {@code true}, may be {@code null} - * @param falseValue the value to match for {@code false}, may be {@code null} + * @param value The Integer to convert + * @param trueValue The value to match for {@code true}, may be {@code null} + * @param falseValue The value to match for {@code false}, may be {@code null} * @return {@code true} or {@code false} * @throws IllegalArgumentException if no match */ @@ -311,238 +483,235 @@ public static boolean toBoolean(final Integer value, final Integer trueValue, fi } /** - *

    Converts an int to a Boolean specifying the conversion values.

    + * Converts a String to a boolean (optimized for performance). * - *

    NOTE: This method may return {@code null} and may throw a {@code NullPointerException} - * if unboxed to a {@code boolean}.

    + *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} + * (case insensitive) will return {@code true}. Otherwise, + * {@code false} is returned.

    * - *

    The checks are done first for the {@code trueValue}, then for the {@code falseValue} and - * finally for the {@code nullValue}.

    + *

    This method performs 4 times faster (JDK1.4) than + * {@code Boolean.valueOf(String)}. However, this method accepts + * 'on' and 'yes', 't', 'y' as true values. * *

    -     *   BooleanUtils.toBooleanObject(0, 0, 2, 3) = Boolean.TRUE
    -     *   BooleanUtils.toBooleanObject(0, 0, 0, 3) = Boolean.TRUE
    -     *   BooleanUtils.toBooleanObject(0, 0, 0, 0) = Boolean.TRUE
    -     *   BooleanUtils.toBooleanObject(2, 1, 2, 3) = Boolean.FALSE
    -     *   BooleanUtils.toBooleanObject(2, 1, 2, 2) = Boolean.FALSE
    -     *   BooleanUtils.toBooleanObject(3, 1, 2, 3) = null
    +     *   BooleanUtils.toBoolean(null)    = false
    +     *   BooleanUtils.toBoolean("true")  = true
    +     *   BooleanUtils.toBoolean("TRUE")  = true
    +     *   BooleanUtils.toBoolean("tRUe")  = true
    +     *   BooleanUtils.toBoolean("on")    = true
    +     *   BooleanUtils.toBoolean("yes")   = true
    +     *   BooleanUtils.toBoolean("false") = false
    +     *   BooleanUtils.toBoolean("x gti") = false
    +     *   BooleanUtils.toBoolean("y") = true
    +     *   BooleanUtils.toBoolean("n") = false
    +     *   BooleanUtils.toBoolean("t") = true
    +     *   BooleanUtils.toBoolean("f") = false
          * 
    * - * @param value the Integer to convert - * @param trueValue the value to match for {@code true} - * @param falseValue the value to match for {@code false} - * @param nullValue the value to to match for {@code null} - * @return Boolean.TRUE, Boolean.FALSE, or {@code null} - * @throws IllegalArgumentException if no match + * @param str The String to check + * @return The boolean value of the string, {@code false} if no match or the String is null */ - public static Boolean toBooleanObject(final int value, final int trueValue, final int falseValue, final int nullValue) { - if (value == trueValue) { - return Boolean.TRUE; - } - if (value == falseValue) { - return Boolean.FALSE; - } - if (value == nullValue) { - return null; - } - throw new IllegalArgumentException("The Integer did not match any specified value"); + public static boolean toBoolean(final String str) { + return toBooleanObject(str) == Boolean.TRUE; } /** - *

    Converts an Integer to a Boolean specifying the conversion values.

    - * - *

    NOTE: This method may return {@code null} and may throw a {@code NullPointerException} - * if unboxed to a {@code boolean}.

    + * Converts a String to a Boolean throwing an exception if no match found. * - *

    The checks are done first for the {@code trueValue}, then for the {@code falseValue} and - * finally for the {@code nullValue}.

    - ** *
    -     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.TRUE
    -     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(3)) = Boolean.TRUE
    -     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)) = Boolean.TRUE
    -     *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.FALSE
    -     *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(2)) = Boolean.FALSE
    -     *   BooleanUtils.toBooleanObject(Integer.valueOf(3), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = null
    +     *   BooleanUtils.toBoolean("true", "true", "false")  = true
    +     *   BooleanUtils.toBoolean("false", "true", "false") = false
          * 
    * - * @param value the Integer to convert - * @param trueValue the value to match for {@code true}, may be {@code null} - * @param falseValue the value to match for {@code false}, may be {@code null} - * @param nullValue the value to to match for {@code null}, may be {@code null} - * @return Boolean.TRUE, Boolean.FALSE, or {@code null} - * @throws IllegalArgumentException if no match + * @param str The String to check + * @param trueString The String to match for {@code true} (case-sensitive), may be {@code null} + * @param falseString The String to match for {@code false} (case-sensitive), may be {@code null} + * @return The boolean value of the string + * @throws IllegalArgumentException if the String doesn't match */ - public static Boolean toBooleanObject(final Integer value, final Integer trueValue, final Integer falseValue, final Integer nullValue) { - if (value == null) { - if (trueValue == null) { - return Boolean.TRUE; - } - if (falseValue == null) { - return Boolean.FALSE; + public static boolean toBoolean(final String str, final String trueString, final String falseString) { + if (str == trueString) { + return true; + } + if (str == falseString) { + return false; + } + if (str != null) { + if (str.equals(trueString)) { + return true; } - if (nullValue == null) { - return null; + if (str.equals(falseString)) { + return false; } - } else if (value.equals(trueValue)) { - return Boolean.TRUE; - } else if (value.equals(falseValue)) { - return Boolean.FALSE; - } else if (value.equals(nullValue)) { - return null; } - throw new IllegalArgumentException("The Integer did not match any specified value"); + throw new IllegalArgumentException("The String did not match either specified value"); } - // Boolean to Integer methods - //----------------------------------------------------------------------- /** - *

    Converts a boolean to an int using the convention that - * {@code true} is {@code 1} and {@code false} is {@code 0}.

    + * Converts a Boolean to a boolean handling {@code null}. * *
    -     *   BooleanUtils.toInteger(true)  = 1
    -     *   BooleanUtils.toInteger(false) = 0
    +     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, false)  = true
    +     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, true)   = true
    +     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, true)  = false
    +     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, false) = false
    +     *   BooleanUtils.toBooleanDefaultIfNull(null, true)           = true
    +     *   BooleanUtils.toBooleanDefaultIfNull(null, false)          = false
          * 
    * - * @param bool the boolean to convert - * @return one if {@code true}, zero if {@code false} + * @param bool The boolean object to convert to primitive + * @param valueIfNull The boolean value to return if the parameter {@code bool} is {@code null} + * @return {@code true} or {@code false} */ - public static int toInteger(final boolean bool) { - return bool ? 1 : 0; + public static boolean toBooleanDefaultIfNull(final Boolean bool, final boolean valueIfNull) { + if (bool == null) { + return valueIfNull; + } + return bool.booleanValue(); } /** - *

    Converts a boolean to an Integer using the convention that - * {@code true} is {@code 1} and {@code false} is {@code 0}.

    + * Converts an int to a Boolean using the convention that {@code zero} + * is {@code false}, everything else is {@code true}. * *
    -     *   BooleanUtils.toIntegerObject(true)  = Integer.valueOf(1)
    -     *   BooleanUtils.toIntegerObject(false) = Integer.valueOf(0)
    +     *   BooleanUtils.toBoolean(0) = Boolean.FALSE
    +     *   BooleanUtils.toBoolean(1) = Boolean.TRUE
    +     *   BooleanUtils.toBoolean(2) = Boolean.TRUE
          * 
    * - * @param bool the boolean to convert - * @return one if {@code true}, zero if {@code false} + * @param value The int to convert + * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, + * {@code null} if {@code null} */ - public static Integer toIntegerObject(final boolean bool) { - return bool ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; + public static Boolean toBooleanObject(final int value) { + return value == 0 ? Boolean.FALSE : Boolean.TRUE; } /** - *

    Converts a Boolean to a Integer using the convention that - * {@code zero} is {@code false}.

    + * Converts an int to a Boolean specifying the conversion values. * - *

    {@code null} will be converted to {@code null}.

    + *

    NOTE: This method may return {@code null} and may throw a {@link NullPointerException} + * if unboxed to a {@code boolean}.

    + * + *

    The checks are done first for the {@code trueValue}, then for the {@code falseValue} and + * finally for the {@code nullValue}.

    * *
    -     *   BooleanUtils.toIntegerObject(Boolean.TRUE)  = Integer.valueOf(1)
    -     *   BooleanUtils.toIntegerObject(Boolean.FALSE) = Integer.valueOf(0)
    +     *   BooleanUtils.toBooleanObject(0, 0, 2, 3) = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(0, 0, 0, 3) = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(0, 0, 0, 0) = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(2, 1, 2, 3) = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject(2, 1, 2, 2) = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject(3, 1, 2, 3) = null
          * 
    * - * @param bool the Boolean to convert - * @return one if Boolean.TRUE, zero if Boolean.FALSE, {@code null} if {@code null} + * @param value The Integer to convert + * @param trueValue The value to match for {@code true} + * @param falseValue The value to match for {@code false} + * @param nullValue The value to match for {@code null} + * @return Boolean.TRUE, Boolean.FALSE, or {@code null} + * @throws IllegalArgumentException if no match */ - public static Integer toIntegerObject(final Boolean bool) { - if (bool == null) { + public static Boolean toBooleanObject(final int value, final int trueValue, final int falseValue, final int nullValue) { + if (value == trueValue) { + return Boolean.TRUE; + } + if (value == falseValue) { + return Boolean.FALSE; + } + if (value == nullValue) { return null; } - return bool.booleanValue() ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; + throw new IllegalArgumentException("The Integer did not match any specified value"); } /** - *

    Converts a boolean to an int specifying the conversion values.

    + * Converts an Integer to a Boolean using the convention that {@code zero} + * is {@code false}, every other numeric value is {@code true}. * - *
    -     *   BooleanUtils.toInteger(true, 1, 0)  = 1
    -     *   BooleanUtils.toInteger(false, 1, 0) = 0
    -     * 
    + *

    {@code null} will be converted to {@code null}.

    * - * @param bool the to convert - * @param trueValue the value to return if {@code true} - * @param falseValue the value to return if {@code false} - * @return the appropriate value - */ - public static int toInteger(final boolean bool, final int trueValue, final int falseValue) { - return bool ? trueValue : falseValue; - } - - /** - *

    Converts a Boolean to an int specifying the conversion values.

    + *

    NOTE: This method may return {@code null} and may throw a {@link NullPointerException} + * if unboxed to a {@code boolean}.

    * *
    -     *   BooleanUtils.toInteger(Boolean.TRUE, 1, 0, 2)  = 1
    -     *   BooleanUtils.toInteger(Boolean.FALSE, 1, 0, 2) = 0
    -     *   BooleanUtils.toInteger(null, 1, 0, 2)          = 2
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(0))    = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(1))    = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(null)) = null
          * 
    * - * @param bool the Boolean to convert - * @param trueValue the value to return if {@code true} - * @param falseValue the value to return if {@code false} - * @param nullValue the value to return if {@code null} - * @return the appropriate value + * @param value The Integer to convert + * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, + * {@code null} if {@code null} input */ - public static int toInteger(final Boolean bool, final int trueValue, final int falseValue, final int nullValue) { - if (bool == null) { - return nullValue; + public static Boolean toBooleanObject(final Integer value) { + if (value == null) { + return null; } - return bool.booleanValue() ? trueValue : falseValue; + return value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE; } /** - *

    Converts a boolean to an Integer specifying the conversion values.

    + * Converts an Integer to a Boolean specifying the conversion values. * - *
    -     *   BooleanUtils.toIntegerObject(true, Integer.valueOf(1), Integer.valueOf(0))  = Integer.valueOf(1)
    -     *   BooleanUtils.toIntegerObject(false, Integer.valueOf(1), Integer.valueOf(0)) = Integer.valueOf(0)
    -     * 
    - * - * @param bool the to convert - * @param trueValue the value to return if {@code true}, may be {@code null} - * @param falseValue the value to return if {@code false}, may be {@code null} - * @return the appropriate value - */ - public static Integer toIntegerObject(final boolean bool, final Integer trueValue, final Integer falseValue) { - return bool ? trueValue : falseValue; - } - - /** - *

    Converts a Boolean to an Integer specifying the conversion values.

    + *

    NOTE: This method may return {@code null} and may throw a {@link NullPointerException} + * if unboxed to a {@code boolean}.

    * + *

    The checks are done first for the {@code trueValue}, then for the {@code falseValue} and + * finally for the {@code nullValue}.

    + ** *
    -     *   BooleanUtils.toIntegerObject(Boolean.TRUE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))  = Integer.valueOf(1)
    -     *   BooleanUtils.toIntegerObject(Boolean.FALSE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2)) = Integer.valueOf(0)
    -     *   BooleanUtils.toIntegerObject(null, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))          = Integer.valueOf(2)
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(3)) = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)) = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(2)) = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject(Integer.valueOf(3), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = null
          * 
    * - * @param bool the Boolean to convert - * @param trueValue the value to return if {@code true}, may be {@code null} - * @param falseValue the value to return if {@code false}, may be {@code null} - * @param nullValue the value to return if {@code null}, may be {@code null} - * @return the appropriate value + * @param value The Integer to convert + * @param trueValue The value to match for {@code true}, may be {@code null} + * @param falseValue The value to match for {@code false}, may be {@code null} + * @param nullValue The value to match for {@code null}, may be {@code null} + * @return Boolean.TRUE, Boolean.FALSE, or {@code null} + * @throws IllegalArgumentException if no match */ - public static Integer toIntegerObject(final Boolean bool, final Integer trueValue, final Integer falseValue, final Integer nullValue) { - if (bool == null) { - return nullValue; + public static Boolean toBooleanObject(final Integer value, final Integer trueValue, final Integer falseValue, final Integer nullValue) { + if (value == null) { + if (trueValue == null) { + return Boolean.TRUE; + } + if (falseValue == null) { + return Boolean.FALSE; + } + if (nullValue == null) { + return null; + } + } else if (value.equals(trueValue)) { + return Boolean.TRUE; + } else if (value.equals(falseValue)) { + return Boolean.FALSE; + } else if (value.equals(nullValue)) { + return null; } - return bool.booleanValue() ? trueValue : falseValue; + throw new IllegalArgumentException("The Integer did not match any specified value"); } - // String to Boolean methods - //----------------------------------------------------------------------- /** - *

    Converts a String to a Boolean.

    + * Converts a String to a Boolean. * - *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} - * (case insensitive) will return {@code true}. - * {@code 'false'}, {@code 'off'}, {@code 'n'}, {@code 'f'} or {@code 'no'} - * (case insensitive) will return {@code false}. + *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'}, {@code 'yes'} + * or {@code '1'} (case insensitive) will return {@code true}. + * {@code 'false'}, {@code 'off'}, {@code 'n'}, {@code 'f'}, {@code 'no'} + * or {@code '0'} (case insensitive) will return {@code false}. * Otherwise, {@code null} is returned.

    * - *

    NOTE: This method may return {@code null} and may throw a {@code NullPointerException} + *

    NOTE: This method may return {@code null} and may throw a {@link NullPointerException} * if unboxed to a {@code boolean}.

    * *
    -     *   // N.B. case is not significant
    +     *   // Case is not significant
          *   BooleanUtils.toBooleanObject(null)    = null
          *   BooleanUtils.toBooleanObject("true")  = Boolean.TRUE
          *   BooleanUtils.toBooleanObject("T")     = Boolean.TRUE // i.e. T[RUE]
    @@ -556,22 +725,24 @@ public static Integer toIntegerObject(final Boolean bool, final Integer trueValu
          *   BooleanUtils.toBooleanObject("oFf")   = Boolean.FALSE
          *   BooleanUtils.toBooleanObject("yes")   = Boolean.TRUE
          *   BooleanUtils.toBooleanObject("Y")     = Boolean.TRUE // i.e. Y[ES]
    +     *   BooleanUtils.toBooleanObject("1")     = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject("0")     = Boolean.FALSE
          *   BooleanUtils.toBooleanObject("blue")  = null
          *   BooleanUtils.toBooleanObject("true ") = null // trailing space (too long)
          *   BooleanUtils.toBooleanObject("ono")   = null // does not match on or no
          * 
    * - * @param str the String to check; upper and lower case are treated as the same - * @return the Boolean value of the string, {@code null} if no match or {@code null} input + * @param str The String to check; upper and lower case are treated as the same + * @return The Boolean value of the string, {@code null} if no match or {@code null} input */ public static Boolean toBooleanObject(final String str) { // Previously used equalsIgnoreCase, which was fast for interned 'true'. // Non interned 'true' matched 15 times slower. // - // Optimisation provides same performance as before for interned 'true'. + // Optimization provides same performance as before for interned 'true'. // Similar performance for null, 'false', and other strings not length 2/3/4. // 'true'/'TRUE' match 4 times slower, 'tRUE'/'True' 7 times slower. - if (str == "true") { + if (str == TRUE) { return Boolean.TRUE; } if (str == null) { @@ -581,11 +752,13 @@ public static Boolean toBooleanObject(final String str) { case 1: { final char ch0 = str.charAt(0); if (ch0 == 'y' || ch0 == 'Y' || - ch0 == 't' || ch0 == 'T') { + ch0 == 't' || ch0 == 'T' || + ch0 == '1') { return Boolean.TRUE; } if (ch0 == 'n' || ch0 == 'N' || - ch0 == 'f' || ch0 == 'F') { + ch0 == 'f' || ch0 == 'F' || + ch0 == '0') { return Boolean.FALSE; } break; @@ -594,11 +767,11 @@ public static Boolean toBooleanObject(final String str) { final char ch0 = str.charAt(0); final char ch1 = str.charAt(1); if ((ch0 == 'o' || ch0 == 'O') && - (ch1 == 'n' || ch1 == 'N') ) { + (ch1 == 'n' || ch1 == 'N')) { return Boolean.TRUE; } if ((ch0 == 'n' || ch0 == 'N') && - (ch1 == 'o' || ch1 == 'O') ) { + (ch1 == 'o' || ch1 == 'O')) { return Boolean.FALSE; } break; @@ -609,12 +782,12 @@ public static Boolean toBooleanObject(final String str) { final char ch2 = str.charAt(2); if ((ch0 == 'y' || ch0 == 'Y') && (ch1 == 'e' || ch1 == 'E') && - (ch2 == 's' || ch2 == 'S') ) { + (ch2 == 's' || ch2 == 'S')) { return Boolean.TRUE; } if ((ch0 == 'o' || ch0 == 'O') && (ch1 == 'f' || ch1 == 'F') && - (ch2 == 'f' || ch2 == 'F') ) { + (ch2 == 'f' || ch2 == 'F')) { return Boolean.FALSE; } break; @@ -627,7 +800,7 @@ public static Boolean toBooleanObject(final String str) { if ((ch0 == 't' || ch0 == 'T') && (ch1 == 'r' || ch1 == 'R') && (ch2 == 'u' || ch2 == 'U') && - (ch3 == 'e' || ch3 == 'E') ) { + (ch3 == 'e' || ch3 == 'E')) { return Boolean.TRUE; } break; @@ -642,7 +815,7 @@ public static Boolean toBooleanObject(final String str) { (ch1 == 'a' || ch1 == 'A') && (ch2 == 'l' || ch2 == 'L') && (ch3 == 's' || ch3 == 'S') && - (ch4 == 'e' || ch4 == 'E') ) { + (ch4 == 'e' || ch4 == 'E')) { return Boolean.FALSE; } break; @@ -655,9 +828,9 @@ public static Boolean toBooleanObject(final String str) { } /** - *

    Converts a String to a Boolean throwing an exception if no match.

    + * Converts a String to a Boolean throwing an exception if no match. * - *

    NOTE: This method may return {@code null} and may throw a {@code NullPointerException} + *

    NOTE: This method may return {@code null} and may throw a {@link NullPointerException} * if unboxed to a {@code boolean}.

    * *
    @@ -672,11 +845,11 @@ public static Boolean toBooleanObject(final String str) {
          *   BooleanUtils.toBooleanObject("null", "true", "false", "null")   = null
          * 
    * - * @param str the String to check - * @param trueString the String to match for {@code true} (case sensitive), may be {@code null} - * @param falseString the String to match for {@code false} (case sensitive), may be {@code null} - * @param nullString the String to match for {@code null} (case sensitive), may be {@code null} - * @return the Boolean value of the string, {@code null} if either the String matches {@code nullString} + * @param str The String to check + * @param trueString The String to match for {@code true} (case-sensitive), may be {@code null} + * @param falseString The String to match for {@code false} (case-sensitive), may be {@code null} + * @param nullString The String to match for {@code null} (case-sensitive), may be {@code null} + * @return The Boolean value of the string, {@code null} if either the String matches {@code nullString} * or if {@code null} input and {@code nullString} is {@code null} * @throws IllegalArgumentException if the String doesn't match */ @@ -702,373 +875,309 @@ public static Boolean toBooleanObject(final String str, final String trueString, throw new IllegalArgumentException("The String did not match any specified value"); } - // String to boolean methods - //----------------------------------------------------------------------- /** - *

    Converts a String to a boolean (optimised for performance).

    + * Converts a boolean to an int using the convention that + * {@code true} is {@code 1} and {@code false} is {@code 0}. * - *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} - * (case insensitive) will return {@code true}. Otherwise, - * {@code false} is returned.

    + *
    +     *   BooleanUtils.toInteger(true)  = 1
    +     *   BooleanUtils.toInteger(false) = 0
    +     * 
    * - *

    This method performs 4 times faster (JDK1.4) than - * {@code Boolean.valueOf(String)}. However, this method accepts - * 'on' and 'yes', 't', 'y' as true values. + * @param bool The boolean to convert + * @return one if {@code true}, zero if {@code false} + */ + public static int toInteger(final boolean bool) { + return bool ? 1 : 0; + } + + /** + * Converts a boolean to an int specifying the conversion values. * *

    -     *   BooleanUtils.toBoolean(null)    = false
    -     *   BooleanUtils.toBoolean("true")  = true
    -     *   BooleanUtils.toBoolean("TRUE")  = true
    -     *   BooleanUtils.toBoolean("tRUe")  = true
    -     *   BooleanUtils.toBoolean("on")    = true
    -     *   BooleanUtils.toBoolean("yes")   = true
    -     *   BooleanUtils.toBoolean("false") = false
    -     *   BooleanUtils.toBoolean("x gti") = false
    -     *   BooleanUtils.toBooleanObject("y") = true
    -     *   BooleanUtils.toBooleanObject("n") = false
    -     *   BooleanUtils.toBooleanObject("t") = true
    -     *   BooleanUtils.toBooleanObject("f") = false
    +     *   BooleanUtils.toInteger(true, 1, 0)  = 1
    +     *   BooleanUtils.toInteger(false, 1, 0) = 0
          * 
    * - * @param str the String to check - * @return the boolean value of the string, {@code false} if no match or the String is null + * @param bool The to convert + * @param trueValue The value to return if {@code true} + * @param falseValue The value to return if {@code false} + * @return The appropriate value */ - public static boolean toBoolean(final String str) { - return toBooleanObject(str) == Boolean.TRUE; + public static int toInteger(final boolean bool, final int trueValue, final int falseValue) { + return bool ? trueValue : falseValue; } /** - *

    Converts a String to a Boolean throwing an exception if no match found.

    + * Converts a Boolean to an int specifying the conversion values. * *
    -     *   BooleanUtils.toBoolean("true", "true", "false")  = true
    -     *   BooleanUtils.toBoolean("false", "true", "false") = false
    +     *   BooleanUtils.toInteger(Boolean.TRUE, 1, 0, 2)  = 1
    +     *   BooleanUtils.toInteger(Boolean.FALSE, 1, 0, 2) = 0
    +     *   BooleanUtils.toInteger(null, 1, 0, 2)          = 2
          * 
    * - * @param str the String to check - * @param trueString the String to match for {@code true} (case sensitive), may be {@code null} - * @param falseString the String to match for {@code false} (case sensitive), may be {@code null} - * @return the boolean value of the string - * @throws IllegalArgumentException if the String doesn't match + * @param bool The Boolean to convert + * @param trueValue The value to return if {@code true} + * @param falseValue The value to return if {@code false} + * @param nullValue The value to return if {@code null} + * @return The appropriate value */ - public static boolean toBoolean(final String str, final String trueString, final String falseString) { - if (str == trueString) { - return true; - } else if (str == falseString) { - return false; - } else if (str != null) { - if (str.equals(trueString)) { - return true; - } else if (str.equals(falseString)) { - return false; - } + public static int toInteger(final Boolean bool, final int trueValue, final int falseValue, final int nullValue) { + if (bool == null) { + return nullValue; } - throw new IllegalArgumentException("The String did not match either specified value"); + return bool.booleanValue() ? trueValue : falseValue; } - // Boolean to String methods - //----------------------------------------------------------------------- /** - *

    Converts a Boolean to a String returning {@code 'true'}, - * {@code 'false'}, or {@code null}.

    + * Converts a boolean to an Integer using the convention that + * {@code true} is {@code 1} and {@code false} is {@code 0}. * *
    -     *   BooleanUtils.toStringTrueFalse(Boolean.TRUE)  = "true"
    -     *   BooleanUtils.toStringTrueFalse(Boolean.FALSE) = "false"
    -     *   BooleanUtils.toStringTrueFalse(null)          = null;
    +     *   BooleanUtils.toIntegerObject(true)  = Integer.valueOf(1)
    +     *   BooleanUtils.toIntegerObject(false) = Integer.valueOf(0)
          * 
    * - * @param bool the Boolean to check - * @return {@code 'true'}, {@code 'false'}, or {@code null} + * @param bool The boolean to convert + * @return one if {@code true}, zero if {@code false} */ - public static String toStringTrueFalse(final Boolean bool) { - return toString(bool, "true", "false", null); + public static Integer toIntegerObject(final boolean bool) { + return bool ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; } /** - *

    Converts a Boolean to a String returning {@code 'on'}, - * {@code 'off'}, or {@code null}.

    + * Converts a boolean to an Integer specifying the conversion values. * *
    -     *   BooleanUtils.toStringOnOff(Boolean.TRUE)  = "on"
    -     *   BooleanUtils.toStringOnOff(Boolean.FALSE) = "off"
    -     *   BooleanUtils.toStringOnOff(null)          = null;
    +     *   BooleanUtils.toIntegerObject(true, Integer.valueOf(1), Integer.valueOf(0))  = Integer.valueOf(1)
    +     *   BooleanUtils.toIntegerObject(false, Integer.valueOf(1), Integer.valueOf(0)) = Integer.valueOf(0)
          * 
    * - * @param bool the Boolean to check - * @return {@code 'on'}, {@code 'off'}, or {@code null} + * @param bool The to convert + * @param trueValue The value to return if {@code true}, may be {@code null} + * @param falseValue The value to return if {@code false}, may be {@code null} + * @return The appropriate value */ - public static String toStringOnOff(final Boolean bool) { - return toString(bool, "on", "off", null); + public static Integer toIntegerObject(final boolean bool, final Integer trueValue, final Integer falseValue) { + return bool ? trueValue : falseValue; } /** - *

    Converts a Boolean to a String returning {@code 'yes'}, - * {@code 'no'}, or {@code null}.

    + * Converts a Boolean to an Integer using the convention that + * {@code zero} is {@code false}. + * + *

    {@code null} will be converted to {@code null}.

    * *
    -     *   BooleanUtils.toStringYesNo(Boolean.TRUE)  = "yes"
    -     *   BooleanUtils.toStringYesNo(Boolean.FALSE) = "no"
    -     *   BooleanUtils.toStringYesNo(null)          = null;
    +     *   BooleanUtils.toIntegerObject(Boolean.TRUE)  = Integer.valueOf(1)
    +     *   BooleanUtils.toIntegerObject(Boolean.FALSE) = Integer.valueOf(0)
          * 
    * - * @param bool the Boolean to check - * @return {@code 'yes'}, {@code 'no'}, or {@code null} + * @param bool The Boolean to convert + * @return one if Boolean.TRUE, zero if Boolean.FALSE, {@code null} if {@code null} */ - public static String toStringYesNo(final Boolean bool) { - return toString(bool, "yes", "no", null); + public static Integer toIntegerObject(final Boolean bool) { + if (bool == null) { + return null; + } + return bool.booleanValue() ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; } /** - *

    Converts a Boolean to a String returning one of the input Strings.

    + * Converts a Boolean to an Integer specifying the conversion values. * *
    -     *   BooleanUtils.toString(Boolean.TRUE, "true", "false", null)   = "true"
    -     *   BooleanUtils.toString(Boolean.FALSE, "true", "false", null)  = "false"
    -     *   BooleanUtils.toString(null, "true", "false", null)           = null;
    +     *   BooleanUtils.toIntegerObject(Boolean.TRUE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))  = Integer.valueOf(1)
    +     *   BooleanUtils.toIntegerObject(Boolean.FALSE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2)) = Integer.valueOf(0)
    +     *   BooleanUtils.toIntegerObject(null, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))          = Integer.valueOf(2)
          * 
    * - * @param bool the Boolean to check - * @param trueString the String to return if {@code true}, may be {@code null} - * @param falseString the String to return if {@code false}, may be {@code null} - * @param nullString the String to return if {@code null}, may be {@code null} - * @return one of the three input Strings + * @param bool The Boolean to convert + * @param trueValue The value to return if {@code true}, may be {@code null} + * @param falseValue The value to return if {@code false}, may be {@code null} + * @param nullValue The value to return if {@code null}, may be {@code null} + * @return The appropriate value */ - public static String toString(final Boolean bool, final String trueString, final String falseString, final String nullString) { + public static Integer toIntegerObject(final Boolean bool, final Integer trueValue, final Integer falseValue, final Integer nullValue) { if (bool == null) { - return nullString; + return nullValue; } - return bool.booleanValue() ? trueString : falseString; + return bool.booleanValue() ? trueValue : falseValue; } - // boolean to String methods - //----------------------------------------------------------------------- /** - *

    Converts a boolean to a String returning {@code 'true'} - * or {@code 'false'}.

    + * Converts a boolean to a String returning one of the input Strings. * *
    -     *   BooleanUtils.toStringTrueFalse(true)   = "true"
    -     *   BooleanUtils.toStringTrueFalse(false)  = "false"
    +     *   BooleanUtils.toString(true, "true", "false")   = "true"
    +     *   BooleanUtils.toString(false, "true", "false")  = "false"
          * 
    * - * @param bool the Boolean to check - * @return {@code 'true'}, {@code 'false'}, or {@code null} + * @param bool The Boolean to check + * @param trueString The String to return if {@code true}, may be {@code null} + * @param falseString The String to return if {@code false}, may be {@code null} + * @return one of the two input Strings */ - public static String toStringTrueFalse(final boolean bool) { - return toString(bool, "true", "false"); + public static String toString(final boolean bool, final String trueString, final String falseString) { + return bool ? trueString : falseString; + } + + /** + * Converts a Boolean to a String returning one of the input Strings. + * + *
    +     *   BooleanUtils.toString(Boolean.TRUE, "true", "false", null)   = "true"
    +     *   BooleanUtils.toString(Boolean.FALSE, "true", "false", null)  = "false"
    +     *   BooleanUtils.toString(null, "true", "false", null)           = null;
    +     * 
    + * + * @param bool The Boolean to check + * @param trueString The String to return if {@code true}, may be {@code null} + * @param falseString The String to return if {@code false}, may be {@code null} + * @param nullString The String to return if {@code null}, may be {@code null} + * @return one of the three input Strings + */ + public static String toString(final Boolean bool, final String trueString, final String falseString, final String nullString) { + if (bool == null) { + return nullString; + } + return bool.booleanValue() ? trueString : falseString; } /** - *

    Converts a boolean to a String returning {@code 'on'} - * or {@code 'off'}.

    + * Converts a boolean to a String returning {@code 'on'} + * or {@code 'off'}. * *
          *   BooleanUtils.toStringOnOff(true)   = "on"
          *   BooleanUtils.toStringOnOff(false)  = "off"
          * 
    * - * @param bool the Boolean to check + * @param bool The Boolean to check * @return {@code 'on'}, {@code 'off'}, or {@code null} */ public static String toStringOnOff(final boolean bool) { - return toString(bool, "on", "off"); + return toString(bool, ON, OFF); } /** - *

    Converts a boolean to a String returning {@code 'yes'} - * or {@code 'no'}.

    + * Converts a Boolean to a String returning {@code 'on'}, + * {@code 'off'}, or {@code null}. * *
    -     *   BooleanUtils.toStringYesNo(true)   = "yes"
    -     *   BooleanUtils.toStringYesNo(false)  = "no"
    +     *   BooleanUtils.toStringOnOff(Boolean.TRUE)  = "on"
    +     *   BooleanUtils.toStringOnOff(Boolean.FALSE) = "off"
    +     *   BooleanUtils.toStringOnOff(null)          = null;
          * 
    * - * @param bool the Boolean to check - * @return {@code 'yes'}, {@code 'no'}, or {@code null} + * @param bool The Boolean to check + * @return {@code 'on'}, {@code 'off'}, or {@code null} */ - public static String toStringYesNo(final boolean bool) { - return toString(bool, "yes", "no"); + public static String toStringOnOff(final Boolean bool) { + return toString(bool, ON, OFF, null); } /** - *

    Converts a boolean to a String returning one of the input Strings.

    + * Converts a boolean to a String returning {@code 'true'} + * or {@code 'false'}. * *
    -     *   BooleanUtils.toString(true, "true", "false")   = "true"
    -     *   BooleanUtils.toString(false, "true", "false")  = "false"
    +     *   BooleanUtils.toStringTrueFalse(true)   = "true"
    +     *   BooleanUtils.toStringTrueFalse(false)  = "false"
          * 
    * - * @param bool the Boolean to check - * @param trueString the String to return if {@code true}, may be {@code null} - * @param falseString the String to return if {@code false}, may be {@code null} - * @return one of the two input Strings + * @param bool The Boolean to check + * @return {@code 'true'}, {@code 'false'}, or {@code null} */ - public static String toString(final boolean bool, final String trueString, final String falseString) { - return bool ? trueString : falseString; + public static String toStringTrueFalse(final boolean bool) { + return toString(bool, TRUE, FALSE); } - // logical operations - // ---------------------------------------------------------------------- /** - *

    Performs an 'and' operation on a set of booleans.

    + * Converts a Boolean to a String returning {@code 'true'}, + * {@code 'false'}, or {@code null}. * *
    -     *   BooleanUtils.and(true, true)         = true
    -     *   BooleanUtils.and(false, false)       = false
    -     *   BooleanUtils.and(true, false)        = false
    -     *   BooleanUtils.and(true, true, false)  = false
    -     *   BooleanUtils.and(true, true, true)   = true
    +     *   BooleanUtils.toStringTrueFalse(Boolean.TRUE)  = "true"
    +     *   BooleanUtils.toStringTrueFalse(Boolean.FALSE) = "false"
    +     *   BooleanUtils.toStringTrueFalse(null)          = null;
          * 
    * - * @param array an array of {@code boolean}s - * @return the result of the logical 'and' operation. That is {@code false} - * if any of the parameters is {@code false} and {@code true} otherwise. - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - * @since 3.0.1 + * @param bool The Boolean to check + * @return {@code 'true'}, {@code 'false'}, or {@code null} */ - public static boolean and(final boolean... array) { - // Validates input - if (array == null) { - throw new IllegalArgumentException("The Array must not be null"); - } - if (array.length == 0) { - throw new IllegalArgumentException("Array is empty"); - } - for (final boolean element : array) { - if (!element) { - return false; - } - } - return true; + public static String toStringTrueFalse(final Boolean bool) { + return toString(bool, TRUE, FALSE, null); } /** - *

    Performs an 'and' operation on an array of Booleans.

    + * Converts a boolean to a String returning {@code 'yes'} + * or {@code 'no'}. * *
    -     *   BooleanUtils.and(Boolean.TRUE, Boolean.TRUE)                 = Boolean.TRUE
    -     *   BooleanUtils.and(Boolean.FALSE, Boolean.FALSE)               = Boolean.FALSE
    -     *   BooleanUtils.and(Boolean.TRUE, Boolean.FALSE)                = Boolean.FALSE
    -     *   BooleanUtils.and(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)   = Boolean.TRUE
    -     *   BooleanUtils.and(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE) = Boolean.FALSE
    -     *   BooleanUtils.and(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE)  = Boolean.FALSE
    +     *   BooleanUtils.toStringYesNo(true)   = "yes"
    +     *   BooleanUtils.toStringYesNo(false)  = "no"
          * 
    * - * @param array an array of {@code Boolean}s - * @return the result of the logical 'and' operation. That is {@code false} - * if any of the parameters is {@code false} and {@code true} otherwise. - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - * @throws IllegalArgumentException if {@code array} contains a {@code null} - * @since 3.0.1 + * @param bool The Boolean to check + * @return {@code 'yes'}, {@code 'no'}, or {@code null} */ - public static Boolean and(final Boolean... array) { - if (array == null) { - throw new IllegalArgumentException("The Array must not be null"); - } - if (array.length == 0) { - throw new IllegalArgumentException("Array is empty"); - } - try { - final boolean[] primitive = ArrayUtils.toPrimitive(array); - return and(primitive) ? Boolean.TRUE : Boolean.FALSE; - } catch (final NullPointerException ex) { - throw new IllegalArgumentException("The array must not contain any null elements"); - } + public static String toStringYesNo(final boolean bool) { + return toString(bool, YES, NO); } /** - *

    Performs an 'or' operation on a set of booleans.

    + * Converts a Boolean to a String returning {@code 'yes'}, + * {@code 'no'}, or {@code null}. * *
    -     *   BooleanUtils.or(true, true)          = true
    -     *   BooleanUtils.or(false, false)        = false
    -     *   BooleanUtils.or(true, false)         = true
    -     *   BooleanUtils.or(true, true, false)   = true
    -     *   BooleanUtils.or(true, true, true)    = true
    -     *   BooleanUtils.or(false, false, false) = false
    +     *   BooleanUtils.toStringYesNo(Boolean.TRUE)  = "yes"
    +     *   BooleanUtils.toStringYesNo(Boolean.FALSE) = "no"
    +     *   BooleanUtils.toStringYesNo(null)          = null;
          * 
    * - * @param array an array of {@code boolean}s - * @return {@code true} if any of the arguments is {@code true}, and it returns {@code false} otherwise. - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - * @since 3.0.1 + * @param bool The Boolean to check + * @return {@code 'yes'}, {@code 'no'}, or {@code null} */ - public static boolean or(final boolean... array) { - if (array == null) { - throw new IllegalArgumentException("The Array must not be null"); - } - if (array.length == 0) { - throw new IllegalArgumentException("Array is empty"); - } - for (final boolean element : array) { - if (element) { - return true; - } - } - return false; + public static String toStringYesNo(final Boolean bool) { + return toString(bool, YES, NO, null); } /** - *

    Performs an 'or' operation on an array of Booleans.

    - * - *
    -     *   BooleanUtils.or(Boolean.TRUE, Boolean.TRUE)                  = Boolean.TRUE
    -     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE)                = Boolean.FALSE
    -     *   BooleanUtils.or(Boolean.TRUE, Boolean.FALSE)                 = Boolean.TRUE
    -     *   BooleanUtils.or(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)    = Boolean.TRUE
    -     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE)  = Boolean.TRUE
    -     *   BooleanUtils.or(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE)   = Boolean.TRUE
    -     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE, Boolean.FALSE) = Boolean.FALSE
    -     * 
    + * Returns an unmodifiable list of Booleans {@code [false, true]}. * - * @param array an array of {@code Boolean}s - * @return {@code true} if any of the arguments is {@code true}, and it returns {@code false} otherwise. - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - * @throws IllegalArgumentException if {@code array} contains a {@code null} - * @since 3.0.1 + * @return An unmodifiable list of Booleans {@code [false, true]}. + * @since 3.13.0 */ - public static Boolean or(final Boolean... array) { - if (array == null) { - throw new IllegalArgumentException("The Array must not be null"); - } - if (array.length == 0) { - throw new IllegalArgumentException("Array is empty"); - } - try { - final boolean[] primitive = ArrayUtils.toPrimitive(array); - return or(primitive) ? Boolean.TRUE : Boolean.FALSE; - } catch (final NullPointerException ex) { - throw new IllegalArgumentException("The array must not contain any null elements"); - } + public static List values() { + return BOOLEAN_LIST; } /** - *

    Performs an xor on a set of booleans.

    + * Performs an xor on a set of booleans. + *

    + * This behaves like an XOR gate; + * it returns true if the number of true values is odd, + * and false if the number of true values is zero or even. + *

    * *
    -     *   BooleanUtils.xor(true, true)   = false
    -     *   BooleanUtils.xor(false, false) = false
    -     *   BooleanUtils.xor(true, false)  = true
    +     *   BooleanUtils.xor(true, true)             = false
    +     *   BooleanUtils.xor(false, false)           = false
    +     *   BooleanUtils.xor(true, false)            = true
    +     *   BooleanUtils.xor(true, false, false)     = true
    +     *   BooleanUtils.xor(true, true, true)       = true
    +     *   BooleanUtils.xor(true, true, true, true) = false
          * 
    * - * @param array an array of {@code boolean}s - * @return the result of the xor operations - * @throws IllegalArgumentException if {@code array} is {@code null} + * @param array An array of {@code boolean}s + * @return true if the number of true values in the array is odd; otherwise returns false. + * @throws NullPointerException if {@code array} is {@code null} * @throws IllegalArgumentException if {@code array} is empty. */ public static boolean xor(final boolean... array) { - if (array == null) { - throw new IllegalArgumentException("The Array must not be null"); - } - if (array.length == 0) { - throw new IllegalArgumentException("Array is empty"); - } - + ObjectUtils.requireNonEmpty(array, "array"); // false if the neutral element of the xor operator boolean result = false; for (final boolean element : array) { @@ -1079,51 +1188,41 @@ public static boolean xor(final boolean... array) { } /** - *

    Performs an xor on an array of Booleans.

    - * + * Performs an xor on an array of Booleans. *
    -     *   BooleanUtils.xor(new Boolean[] { Boolean.TRUE, Boolean.TRUE })   = Boolean.FALSE
    -     *   BooleanUtils.xor(new Boolean[] { Boolean.FALSE, Boolean.FALSE }) = Boolean.FALSE
    -     *   BooleanUtils.xor(new Boolean[] { Boolean.TRUE, Boolean.FALSE })  = Boolean.TRUE
    -     *   BooleanUtils.xor(Boolean.TRUE, Boolean.FALSE, Boolean.FALSE)     = Boolean.TRUE
    +     *   BooleanUtils.xor(Boolean.TRUE, Boolean.TRUE)                 = Boolean.FALSE
    +     *   BooleanUtils.xor(Boolean.FALSE, Boolean.FALSE)               = Boolean.FALSE
    +     *   BooleanUtils.xor(Boolean.TRUE, Boolean.FALSE)                = Boolean.TRUE
    +     *   BooleanUtils.xor(Boolean.TRUE, Boolean.FALSE, Boolean.FALSE) = Boolean.TRUE
    +     *   BooleanUtils.xor(Boolean.FALSE, null)                        = Boolean.FALSE
    +     *   BooleanUtils.xor(Boolean.TRUE, null)                         = Boolean.TRUE
          * 
    + *

    + * Null array elements map to false, like {@code Boolean.parseBoolean(null)} and its callers return false. + *

    * - * @param array an array of {@code Boolean}s - * @return the result of the xor operations - * @throws IllegalArgumentException if {@code array} is {@code null} + * @param array An array of {@link Boolean}s + * @return The result of the xor operations + * @throws NullPointerException if {@code array} is {@code null} * @throws IllegalArgumentException if {@code array} is empty. - * @throws IllegalArgumentException if {@code array} contains a {@code null} */ public static Boolean xor(final Boolean... array) { - if (array == null) { - throw new IllegalArgumentException("The Array must not be null"); - } - if (array.length == 0) { - throw new IllegalArgumentException("Array is empty"); - } - try { - final boolean[] primitive = ArrayUtils.toPrimitive(array); - return xor(primitive) ? Boolean.TRUE : Boolean.FALSE; - } catch (final NullPointerException ex) { - throw new IllegalArgumentException("The array must not contain any null elements"); - } + ObjectUtils.requireNonEmpty(array, "array"); + return xor(ArrayUtils.toPrimitive(array)) ? Boolean.TRUE : Boolean.FALSE; } /** - *

    Compares two {@code boolean} values. This is the same functionality as provided in Java 7.

    + * {@link BooleanUtils} instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code BooleanUtils.negate(true);}. * - * @param x the first {@code boolean} to compare - * @param y the second {@code boolean} to compare - * @return the value {@code 0} if {@code x == y}; - * a value less than {@code 0} if {@code !x && y}; and - * a value greater than {@code 0} if {@code x && !y} - * @since 3.4 + *

    This constructor is public to permit tools that require a JavaBean instance + * to operate.

    + * + * @deprecated TODO Make private in 4.0. */ - public static int compare(final boolean x, final boolean y) { - if (x == y) { - return 0; - } - return x ? 1 : -1; + @Deprecated + public BooleanUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/CachedRandomBits.java b/src/main/java/org/apache/commons/lang3/CachedRandomBits.java new file mode 100644 index 00000000000..e3e7f008ad0 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/CachedRandomBits.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Objects; +import java.util.Random; + +/** + * Generates random integers of specific bit length. + * + *

    + * It is more efficient than calling Random.nextInt(1 << nbBits). It uses a cache of cacheSize random bytes that it replenishes when it gets empty. This is + * especially beneficial for SecureRandom Drbg implementations, which incur a constant cost at each randomness generation. + *

    + * + *

    + * Used internally by RandomStringUtils. + *

    + * + *

    + * #NotThreadSafe# + *

    + */ +final class CachedRandomBits { + + /** + * The maximum size of the cache. + * + *

    + * This is to prevent the possibility of overflow in the {@code if (bitIndex >> 3 >= cache.length)} in the {@link #nextBits(int)} method. + *

    + */ + private static final int MAX_CACHE_SIZE = Integer.MAX_VALUE >> 3; + + /** Maximum number of bits that can be generated (size of an int) */ + private static final int MAX_BITS = 32; + + /** Mask to extract the bit offset within a byte (0-7) */ + private static final int BIT_INDEX_MASK = 0x7; + + /** Number of bits in a byte */ + private static final int BITS_PER_BYTE = 8; + private final Random random; + private final byte[] cache; + + /** + * Index of the next bit in the cache to be used. + * + *
      + *
    • bitIndex=0 means the cache is fully random and none of the bits have been used yet.
    • + *
    • bitIndex=1 means that only the LSB of cache[0] has been used and all other bits can be used.
    • + *
    • bitIndex=8 means that only the 8 bits of cache[0] has been used.
    • + *
    + */ + private int bitIndex; + + /** + * Creates a new instance. + * + * @param cacheSize number of bytes cached (only affects performance) + * @param random random source + */ + CachedRandomBits(final int cacheSize, final Random random) { + if (cacheSize <= 0) { + throw new IllegalArgumentException("cacheSize must be positive"); + } + this.cache = cacheSize <= MAX_CACHE_SIZE ? new byte[cacheSize] : new byte[MAX_CACHE_SIZE]; + this.random = Objects.requireNonNull(random, "random"); + this.random.nextBytes(this.cache); + this.bitIndex = 0; + } + + /** + * Generates a random integer with the specified number of bits. + * + *

    This method efficiently generates random bits by using a byte cache and bit manipulation: + *

      + *
    • Uses a byte array cache to avoid frequent calls to the underlying random number generator
    • + *
    • Extracts bits from each byte using bit shifting and masking
    • + *
    • Handles partial bytes to avoid wasting random bits
    • + *
    • Accumulates bits until the requested number is reached
    • + *
    + *

    + * + * @param bits number of bits to generate, MUST be between 1 and 32 (inclusive) + * @return random integer containing exactly the requested number of random bits + * @throws IllegalArgumentException if bits is not between 1 and 32 + */ + public int nextBits(final int bits) { + if (bits > MAX_BITS || bits <= 0) { + throw new IllegalArgumentException("number of bits must be between 1 and " + MAX_BITS); + } + int result = 0; + int generatedBits = 0; // number of generated bits up to now + while (generatedBits < bits) { + // Check if we need to refill the cache + // Convert bitIndex to byte index by dividing by 8 (right shift by 3) + if (bitIndex >> 3 >= cache.length) { + // We exhausted the number of bits in the cache + // This should only happen if the bitIndex is exactly matching the cache length + assert bitIndex == cache.length * BITS_PER_BYTE; + random.nextBytes(cache); + bitIndex = 0; + } + // Calculate how many bits we can extract from the current byte + // 1. Get current position within byte (0-7) using bitIndex & 0x7 + // 2. Calculate remaining bits in byte: 8 - (position within byte) + // 3. Take minimum of remaining bits in byte and bits still needed + final int generatedBitsInIteration = Math.min( + BITS_PER_BYTE - (bitIndex & BIT_INDEX_MASK), + bits - generatedBits); + // Shift existing result left to make room for new bits + result = result << generatedBitsInIteration; + // Extract and append new bits: + // 1. Get byte from cache (bitIndex >> 3 converts bit index to byte index) + // 2. Shift right by bit position within byte (bitIndex & 0x7) + // 3. Mask to keep only the bits we want ((1 << generatedBitsInIteration) - 1) + result |= cache[bitIndex >> 3] >> (bitIndex & BIT_INDEX_MASK) & (1 << generatedBitsInIteration) - 1; + // Update counters + generatedBits += generatedBitsInIteration; + bitIndex += generatedBitsInIteration; + } + return result; + } +} diff --git a/src/main/java/org/apache/commons/lang3/CharEncoding.java b/src/main/java/org/apache/commons/lang3/CharEncoding.java index 9f0d2e0ca55..75c61931a18 100644 --- a/src/main/java/org/apache/commons/lang3/CharEncoding.java +++ b/src/main/java/org/apache/commons/lang3/CharEncoding.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,18 +19,19 @@ import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.StandardCharsets; /** - *

    Character encoding names required of every implementation of the Java platform.

    + * Character encoding names required of every implementation of the Java platform. * - *

    According to JRE character + *

    According to JRE character * encoding names:

    * *

    Every implementation of the Java platform is required to support the following character encodings. * Consult the release documentation for your implementation to see if any other encodings are supported. *

    * - * @see JRE character encoding names + * @see JRE character encoding names * @since 2.1 * @deprecated Java 7 introduced {@link java.nio.charset.StandardCharsets}, which defines these constants as * {@link Charset} objects. Use {@link Charset#name()} to get the string values provided in this class. @@ -40,57 +41,57 @@ public class CharEncoding { /** - *

    ISO Latin Alphabet #1, also known as ISO-LATIN-1.

    + * ISO Latin Alphabet #1, also known as ISO-LATIN-1. * *

    Every implementation of the Java platform is required to support this character encoding.

    */ - public static final String ISO_8859_1 = "ISO-8859-1"; + public static final String ISO_8859_1 = StandardCharsets.ISO_8859_1.name(); /** - *

    Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block - * of the Unicode character set.

    + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block + * of the Unicode character set. * *

    Every implementation of the Java platform is required to support this character encoding.

    */ - public static final String US_ASCII = "US-ASCII"; + public static final String US_ASCII = StandardCharsets.US_ASCII.name(); /** - *

    Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial - * byte-order mark (either order accepted on input, big-endian used on output).

    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial + * byte-order mark (either order accepted on input, big-endian used on output). * *

    Every implementation of the Java platform is required to support this character encoding.

    */ - public static final String UTF_16 = "UTF-16"; + public static final String UTF_16 = StandardCharsets.UTF_16.name(); /** - *

    Sixteen-bit Unicode Transformation Format, big-endian byte order.

    + * Sixteen-bit Unicode Transformation Format, big-endian byte order. * *

    Every implementation of the Java platform is required to support this character encoding.

    */ - public static final String UTF_16BE = "UTF-16BE"; + public static final String UTF_16BE = StandardCharsets.UTF_16BE.name(); /** - *

    Sixteen-bit Unicode Transformation Format, little-endian byte order.

    + * Sixteen-bit Unicode Transformation Format, little-endian byte order. * *

    Every implementation of the Java platform is required to support this character encoding.

    */ - public static final String UTF_16LE = "UTF-16LE"; + public static final String UTF_16LE = StandardCharsets.UTF_16LE.name(); /** - *

    Eight-bit Unicode Transformation Format.

    + * Eight-bit Unicode Transformation Format. * *

    Every implementation of the Java platform is required to support this character encoding.

    */ - public static final String UTF_8 = "UTF-8"; + public static final String UTF_8 = StandardCharsets.UTF_8.name(); /** - *

    Returns whether the named charset is supported.

    + * Tests whether the named charset is supported. * *

    This is similar to + * href="https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#isSupported%28java.lang.String%29"> * java.nio.charset.Charset.isSupported(String) but handles more formats

    * - * @param name the name of the requested charset; may be either a canonical name or an alias, null returns false + * @param name The name of the requested charset; may be either a canonical name or an alias, null returns false * @return {@code true} if the charset is available in the current Java virtual machine * @deprecated Please use {@link Charset#isSupported(String)} instead, although be aware that {@code null} * values are not accepted by that method and an {@link IllegalCharsetNameException} may be thrown. @@ -107,4 +108,13 @@ public static boolean isSupported(final String name) { } } + /** + * Constructs a new instance. + * + * @deprecated Will be removed in 4.0.0. + */ + @Deprecated + public CharEncoding() { + // empty + } } diff --git a/src/main/java/org/apache/commons/lang3/CharRange.java b/src/main/java/org/apache/commons/lang3/CharRange.java index 6a416bb632a..b1dbb9816cc 100644 --- a/src/main/java/org/apache/commons/lang3/CharRange.java +++ b/src/main/java/org/apache/commons/lang3/CharRange.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,72 +16,138 @@ */ package org.apache.commons.lang3; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Objects; /** - *

    A contiguous range of characters, optionally negated.

    + * A contiguous range of characters, optionally negated. * *

    Instances are immutable.

    * *

    #ThreadSafe#

    + * * @since 1.0 + * @since 3.21.0 {@code serialVersionUID} changed from {@code 8270183163158333422L} to {@code 2L}. */ // TODO: This is no longer public and will be removed later as CharSet is moved // to depend on Range. final class CharRange implements Iterable, Serializable { /** - * Required for serialization support. Lang version 2.0. - * - * @see java.io.Serializable + * Character {@link Iterator}. + *

    #NotThreadSafe#

    */ - private static final long serialVersionUID = 8270183163158333422L; + private static final class CharacterIterator implements Iterator { - /** The first character, inclusive, in the range. */ - private final char start; - /** The last character, inclusive, in the range. */ - private final char end; - /** True if the range is everything except the characters specified. */ - private final boolean negated; + /** The current character */ + private char current; - /** Cached toString. */ - private transient String iToString; + private final CharRange range; + private boolean hasNext; + + /** + * Constructs a new iterator for the character range. + * + * @param r The character range. + */ + private CharacterIterator(final CharRange r) { + range = r; + hasNext = true; + if (range.isEmpty()) { + // This range is an empty set + hasNext = false; + } else if (range.negated) { + if (range.isStartMin()) { + current = (char) (range.end + 1); + } else { + current = Character.MIN_VALUE; + } + } else { + current = range.start; + } + } + + /** + * Tests whether this iterator reached the end character. + * + * @return {@code true} if the iterator has yet to reach the character date. + */ + @Override + public boolean hasNext() { + return hasNext; + } + + /** + * Returns the next character in the iteration. + * + * @return {@link Character} for the next character. + */ + @Override + public Character next() { + if (!hasNext) { + throw new NoSuchElementException(); + } + final char cur = current; + prepareNext(); + return Character.valueOf(cur); + } + + /** + * Prepares the next character in the range. + */ + private void prepareNext() { + if (range.negated) { + if (current == Character.MAX_VALUE) { + hasNext = false; + } else if (current + 1 == range.start) { + if (range.isEndMax()) { + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = (char) (current + 1); + } + } else if (current < range.end) { + current = (char) (current + 1); + } else { + hasNext = false; + } + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException Always thrown. + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } /** - *

    Constructs a {@code CharRange} over a set of characters, - * optionally negating the range.

    - * - *

    A negated range includes everything except that defined by the - * start and end characters.

    - * - *

    If start and end are in the wrong order, they are reversed. - * Thus {@code a-e} is the same as {@code e-a}.

    + * Required for serialization support. Lang version 2.0. * - * @param start first character, inclusive, in this range - * @param end last character, inclusive, in this range - * @param negated true to express everything except the range + * @see java.io.Serializable + * @since 3.21.0 {@code serialVersionUID} changed from {@code 8270183163158333422L} to {@value}. */ - private CharRange(char start, char end, final boolean negated) { - super(); - if (start > end) { - final char temp = start; - start = end; - end = temp; - } + private static final long serialVersionUID = 2L; - this.start = start; - this.end = end; - this.negated = negated; - } + /** Empty array. */ + static final CharRange[] EMPTY_ARRAY = {}; /** - *

    Constructs a {@code CharRange} over a single character.

    + * Constructs a {@link CharRange} over a single character. * - * @param ch only character in this range - * @return the new CharRange object - * @see CharRange#CharRange(char, char, boolean) + * @param ch only character in this range. + * @return The new CharRange object. * @since 2.5 */ public static CharRange is(final char ch) { @@ -89,97 +155,110 @@ public static CharRange is(final char ch) { } /** - *

    Constructs a negated {@code CharRange} over a single character.

    + * Constructs a {@link CharRange} over a set of characters. * - * @param ch only character in this range - * @return the new CharRange object - * @see CharRange#CharRange(char, char, boolean) + *

    If start and end are in the wrong order, they are reversed. + * Thus {@code a-e} is the same as {@code e-a}.

    + * + * @param start first character, inclusive, in this range. + * @param end last character, inclusive, in this range. + * @return The new CharRange object. * @since 2.5 */ - public static CharRange isNot(final char ch) { - return new CharRange(ch, ch, true); + public static CharRange isIn(final char start, final char end) { + return new CharRange(start, end, false); } /** - *

    Constructs a {@code CharRange} over a set of characters.

    + * Constructs a negated {@link CharRange} over a single character. + * + *

    A negated range includes everything except that defined by the + * single character.

    * - * @param start first character, inclusive, in this range - * @param end last character, inclusive, in this range - * @return the new CharRange object - * @see CharRange#CharRange(char, char, boolean) + * @param ch only character in this range. + * @return The new CharRange object. * @since 2.5 */ - public static CharRange isIn(final char start, final char end) { - return new CharRange(start, end, false); + public static CharRange isNot(final char ch) { + return new CharRange(ch, ch, true); } /** - *

    Constructs a negated {@code CharRange} over a set of characters.

    + * Constructs a negated {@link CharRange} over a set of characters. + * + *

    A negated range includes everything except that defined by the + * start and end characters.

    + * + *

    If start and end are in the wrong order, they are reversed. + * Thus {@code a-e} is the same as {@code e-a}.

    * - * @param start first character, inclusive, in this range - * @param end last character, inclusive, in this range - * @return the new CharRange object - * @see CharRange#CharRange(char, char, boolean) + * @param start first character, inclusive, in this range. + * @param end last character, inclusive, in this range. + * @return The new CharRange object. * @since 2.5 */ public static CharRange isNotIn(final char start, final char end) { return new CharRange(start, end, true); } - // Accessors - //----------------------------------------------------------------------- - /** - *

    Gets the start character for this character range.

    - * - * @return the start char (inclusive) - */ - public char getStart() { - return this.start; - } + /** The first character, inclusive, in the range. */ + private final char start; - /** - *

    Gets the end character for this character range.

    - * - * @return the end char (inclusive) - */ - public char getEnd() { - return this.end; - } + /** The last character, inclusive, in the range. */ + private final char end; + + /** True if the range is everything except the characters specified. */ + private final boolean negated; + + /** Cached toString. */ + private transient String iToString; /** - *

    Is this {@code CharRange} negated.

    + * Constructs a {@link CharRange} over a set of characters, + * optionally negating the range. * *

    A negated range includes everything except that defined by the * start and end characters.

    * - * @return {@code true} if negated + *

    If start and end are in the wrong order, they are reversed. + * Thus {@code a-e} is the same as {@code e-a}.

    + * + * @param start first character, inclusive, in this range. + * @param end last character, inclusive, in this range. + * @param negated true to express everything except the range. */ - public boolean isNegated() { - return negated; + private CharRange(char start, char end, final boolean negated) { + if (start > end) { + final char temp = start; + start = end; + end = temp; + } + + this.start = start; + this.end = end; + this.negated = negated; } - // Contains - //----------------------------------------------------------------------- /** - *

    Is the character specified contained in this range.

    + * Is the character specified contained in this range. * - * @param ch the character to check - * @return {@code true} if this range contains the input character + * @param ch The character to check. + * @return {@code true} if this range contains the input character. */ public boolean contains(final char ch) { return (ch >= start && ch <= end) != negated; } /** - *

    Are all the characters of the passed in range contained in - * this range.

    + * Are all the characters of the passed in range contained in + * this range. * - * @param range the range to check against - * @return {@code true} if this range entirely contains the input range - * @throws IllegalArgumentException if {@code null} input + * @param range The range to check against. + * @return {@code true} if this range entirely contains the input range. + * @throws NullPointerException if {@code null} input. */ public boolean contains(final CharRange range) { - Validate.isTrue(range != null, "The Range must not be null"); + Objects.requireNonNull(range, "range"); if (negated) { if (range.negated) { return start >= range.start && end <= range.end; @@ -187,19 +266,29 @@ public boolean contains(final CharRange range) { return range.end < start || range.start > end; } if (range.negated) { - return start == 0 && end == Character.MAX_VALUE; + // range denotes [0, range.start - 1] union [range.end + 1, Character.MAX_VALUE] + if (range.isEmpty()) { + return true; // range denotes the empty set + } + if (range.isStartMin()) { + // range denotes [range.end + 1, Character.MAX_VALUE] + return isEndMax() && start <= range.end + 1; + } + if (range.isEndMax()) { + // range denotes [0, range.start - 1] + return isStartMin() && end + 1 >= range.start; + } + return isStartMin() && isEndMax(); } return start <= range.start && end >= range.end; } - // Basics - //----------------------------------------------------------------------- /** - *

    Compares two CharRange objects, returning true if they represent - * exactly the same range of characters defined in the same way.

    + * Compares two CharRange objects, returning true if they represent + * exactly the same range of characters defined in the same way. * - * @param obj the object to compare to - * @return true if equal + * @param obj The object to compare to. + * @return true if equal. */ @Override public boolean equals(final Object obj) { @@ -214,44 +303,73 @@ public boolean equals(final Object obj) { } /** - *

    Gets a hashCode compatible with the equals method.

    + * Gets the end character for this character range. + * + * @return The end char (inclusive). + */ + public char getEnd() { + return this.end; + } + + /** + * Gets the start character for this character range. + * + * @return The start char (inclusive). + */ + public char getStart() { + return this.start; + } + + /** + * Gets a hashCode compatible with the equals method. * - * @return a suitable hashCode + * @return A suitable hashCode. */ @Override public int hashCode() { - return 83 + start + 7 * end + (negated ? 1 : 0); + return Objects.hash(end, negated, start); } /** - *

    Gets a string representation of the character range.

    + * Checks if this range denotes the empty set. * - * @return string representation of this range + *

    A plain (non-negated) range always contains at least one character and is + * therefore never empty. A negated range is empty if and only if it excludes the + * entire character space, i.e. if it was created via + * {@code isNotIn(Character.MIN_VALUE, Character.MAX_VALUE)}.

    + * + * @return {@code true} if this range contains no characters, {@code false} otherwise. */ - @Override - public String toString() { - if (iToString == null) { - final StringBuilder buf = new StringBuilder(4); - if (isNegated()) { - buf.append('^'); - } - buf.append(start); - if (start != end) { - buf.append('-'); - buf.append(end); - } - iToString = buf.toString(); - } - return iToString; + boolean isEmpty() { + return negated && isStartMin() && isEndMax(); + } + + private boolean isEndMax() { + return end == Character.MAX_VALUE; + } + + /** + * Is this {@link CharRange} negated. + * + *

    A negated range includes everything except that defined by the + * start and end characters.

    + * + * @return {@code true} if negated. + */ + public boolean isNegated() { + return negated; + } + + private boolean isStartMin() { + return start == Character.MIN_VALUE; } - // Expansions - //----------------------------------------------------------------------- /** - *

    Returns an iterator which can be used to walk through the characters described by this range.

    + * Returns an iterator which can be used to walk through the characters described by this range. * *

    #NotThreadSafe# the iterator is not thread-safe

    - * @return an iterator to the chars represented by this range + * + * @return An iterator to the chars represented by this range * @since 2.5 */ @Override @@ -260,98 +378,40 @@ public Iterator iterator() { } /** - * Character {@link Iterator}. - *

    #NotThreadSafe#

    + * Re-asserts the {@code start <= end} invariant after default deserialization. The constructor reverses reversed endpoints, so a legitimately serialized + * instance always has {@code start <= end}; a stream that violates this did not come from the constructor and is rejected. + * + * @param in See {@link Serializable}. + * @throws IOException See {@link Serializable}. + * @throws ClassNotFoundException See {@link Serializable}. + * @throws InvalidObjectException If {@code start} is greater than {@code end}. */ - private static class CharacterIterator implements Iterator { - /** The current character */ - private char current; - - private final CharRange range; - private boolean hasNext; - - /** - * Construct a new iterator for the character range. - * - * @param r The character range - */ - private CharacterIterator(final CharRange r) { - range = r; - hasNext = true; - - if (range.negated) { - if (range.start == 0) { - if (range.end == Character.MAX_VALUE) { - // This range is an empty set - hasNext = false; - } else { - current = (char) (range.end + 1); - } - } else { - current = 0; - } - } else { - current = range.start; - } + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + if (start > end) { + throw new InvalidObjectException("CharRange start is greater than end."); } + } - /** - * Prepare the next character in the range. - */ - private void prepareNext() { - if (range.negated) { - if (current == Character.MAX_VALUE) { - hasNext = false; - } else if (current + 1 == range.start) { - if (range.end == Character.MAX_VALUE) { - hasNext = false; - } else { - current = (char) (range.end + 1); - } - } else { - current = (char) (current + 1); - } - } else if (current < range.end) { - current = (char) (current + 1); - } else { - hasNext = false; + /** + * Gets a string representation of the character range. + * + * @return string representation of this range. + */ + @Override + public String toString() { + if (iToString == null) { + final StringBuilder buf = new StringBuilder(4); + if (isNegated()) { + buf.append('^'); } - } - - /** - * Has the iterator not reached the end character yet? - * - * @return {@code true} if the iterator has yet to reach the character date - */ - @Override - public boolean hasNext() { - return hasNext; - } - - /** - * Return the next character in the iteration - * - * @return {@code Character} for the next character - */ - @Override - public Character next() { - if (!hasNext) { - throw new NoSuchElementException(); + buf.append(start); + if (start != end) { + buf.append('-'); + buf.append(end); } - final char cur = current; - prepareNext(); - return Character.valueOf(cur); - } - - /** - * Always throws UnsupportedOperationException. - * - * @throws UnsupportedOperationException - * @see java.util.Iterator#remove() - */ - @Override - public void remove() { - throw new UnsupportedOperationException(); + iToString = buf.toString(); } + return iToString; } } diff --git a/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java b/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java index 58c04536e46..2d07f55bb5f 100644 --- a/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java +++ b/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,8 +17,8 @@ package org.apache.commons.lang3; /** - *

    Operations on {@link CharSequence} that are - * {@code null} safe.

    + * Operations on {@link CharSequence} that are + * {@code null} safe. * * @see CharSequence * @since 3.0 @@ -28,73 +28,120 @@ public class CharSequenceUtils { private static final int NOT_FOUND = -1; /** - *

    {@code CharSequenceUtils} instances should NOT be constructed in - * standard programming.

    - * - *

    This constructor is public to permit tools that require a JavaBean - * instance to operate.

    + * Whether the running JDK folds a supplementary code point split across a surrogate pair when comparing case insensitively in + * {@link String#regionMatches(boolean, int, String, int, int)}. JDKs up to and including Java 11 compare surrogate by surrogate and never match such a + * pair; later JDKs fold the whole code point. Probing what {@link String} actually does (rather than gating on a version constant) keeps every + * {@link CharSequence} type in step with {@link String} on whatever JDK is running. DESERET CAPITAL LETTER LONG I (U+10400) folds to its small form + * (U+10428). */ - public CharSequenceUtils() { - super(); + private static final boolean STRING_FOLDS_SUPPLEMENTARY_CASE = new String(Character.toChars(0x10400)).regionMatches(true, 0, + new String(Character.toChars(0x10428)), 0, 2); + + static final int TO_STRING_LIMIT = 16; + + private static boolean checkLaterThan1(final CharSequence cs, final CharSequence searchChar, final int len2, final int start1) { + for (int i = 1, j = len2 - 1; i <= j; i++, j--) { + if (cs.charAt(start1 + i) != searchChar.charAt(i) || cs.charAt(start1 + j) != searchChar.charAt(j)) { + return false; + } + } + return true; } - //----------------------------------------------------------------------- /** - *

    Returns a new {@code CharSequence} that is a subsequence of this - * sequence starting with the {@code char} value at the specified index.

    + * Tests whether two code points are equal ignoring case, matching the folding used by {@link String#regionMatches(boolean, int, String, int, int)}. * - *

    This provides the {@code CharSequence} equivalent to {@link String#substring(int)}. - * The length (in {@code char}) of the returned sequence is {@code length() - start}, - * so if {@code start == end} then an empty sequence is returned.

    + * @param cp1 The first code point. + * @param cp2 The second code point. + * @return whether the code points are equal ignoring case. + */ + private static boolean equalsIgnoreCase(final int cp1, final int cp2) { + final int u1 = Character.toUpperCase(cp1); + final int u2 = Character.toUpperCase(cp2); + return u1 == u2 || Character.toLowerCase(u1) == Character.toLowerCase(u2); + } + + /** + * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. + *

    + * {@link CharSequence} types without a dedicated branch are scanned in place rather than materialized with {@code toString()}: for builder + * types (for example {@code org.apache.commons.lang3.text.StrBuilder}), {@code toString()} copies the whole buffer, and callers that invoke + * this method once per occurrence (such as {@code deleteAll}/{@code replaceAll}) would multiply that copy into allocation-quadratic churn. + *

    * - * @param cs the specified subsequence, null returns null - * @param start the start index, inclusive, valid - * @return a new subsequence, may be null - * @throws IndexOutOfBoundsException if {@code start} is negative or if - * {@code start} is greater than {@code length()} + * @param cs The {@link CharSequence} to be processed. + * @param searchChar The {@link CharSequence} to be searched for. + * @param start The start index. + * @return The index where the search sequence was found, or {@code -1} if there is no such occurrence. */ - public static CharSequence subSequence(final CharSequence cs, final int start) { - return cs == null ? null : cs.subSequence(start, cs.length()); + static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) { + if (cs == null || searchChar == null) { + return StringUtils.INDEX_NOT_FOUND; + } + if (cs instanceof String) { + return ((String) cs).indexOf(searchChar.toString(), start); + } + if (cs instanceof StringBuilder) { + return ((StringBuilder) cs).indexOf(searchChar.toString(), start); + } + if (cs instanceof StringBuffer) { + return ((StringBuffer) cs).indexOf(searchChar.toString(), start); + } + // Direct scan without copying cs; matches the semantics of String.indexOf(String, int). + final int len1 = cs.length(); + final int len2 = searchChar.length(); + final int from = Math.max(start, 0); + if (len2 == 0) { + return Math.min(from, len1); + } + if (len2 > len1 - from) { + return StringUtils.INDEX_NOT_FOUND; + } + final char char0 = searchChar.charAt(0); + final int max = len1 - len2; + for (int i = from; i <= max; i++) { + if (cs.charAt(i) == char0 && checkLaterThan1(cs, searchChar, len2, i)) { + return i; + } + } + return StringUtils.INDEX_NOT_FOUND; } - //----------------------------------------------------------------------- /** - * Returns the index within {@code cs} of the first occurrence of the - * specified character, starting the search at the specified index. + * Returns the index within {@code cs} of the first occurrence of the specified character, starting the search at the specified index. *

    - * If a character with value {@code searchChar} occurs in the - * character sequence represented by the {@code cs} - * object at an index no smaller than {@code start}, then - * the index of the first such occurrence is returned. For values - * of {@code searchChar} in the range from 0 to 0xFFFF (inclusive), - * this is the smallest value k such that: - *

    -     * (this.charAt(k) == searchChar) && (k >= start)
    -     * 
    - * is true. For other values of {@code searchChar}, it is the - * smallest value k such that: - *
    -     * (this.codePointAt(k) == searchChar) && (k >= start)
    -     * 
    - * is true. In either case, if no such character occurs inm {@code cs} - * at or after position {@code start}, then - * {@code -1} is returned. + * If a character with value {@code searchChar} occurs in the character sequence represented by the {@code cs} object at an index no smaller than + * {@code start}, then the index of the first such occurrence is returned. For values of {@code searchChar} in the range from 0 to 0xFFFF (inclusive), this + * is the smallest value k such that: + *

    * + *
    +     * (this.charAt(k) == searchChar) && (k >= start)
    +     * 
    *

    - * There is no restriction on the value of {@code start}. If it - * is negative, it has the same effect as if it were zero: the entire - * {@code CharSequence} may be searched. If it is greater than - * the length of {@code cs}, it has the same effect as if it were - * equal to the length of {@code cs}: {@code -1} is returned. + * is true. For other values of {@code searchChar}, it is the smallest value k such that: + *

    * - *

    All indices are specified in {@code char} values - * (Unicode code units). + *

    +     * (this.codePointAt(k) == searchChar) && (k >= start)
    +     * 
    + *

    + * is true. In either case, if no such character occurs inm {@code cs} at or after position {@code start}, then {@code -1} is returned. + *

    + *

    + * There is no restriction on the value of {@code start}. If it is negative, it has the same effect as if it were zero: the entire {@link CharSequence} may + * be searched. If it is greater than the length of {@code cs}, it has the same effect as if it were equal to the length of {@code cs}: {@code -1} is + * returned. + *

    + *

    + * All indices are specified in {@code char} values (Unicode code units). + *

    * - * @param cs the {@code CharSequence} to be processed, not null - * @param searchChar the char to be searched for - * @param start the start index, negative starts at the string start - * @return the index where the search char was found, -1 if not found - * @since 3.6 updated to behave more like {@code String} + * @param cs The {@link CharSequence} to be processed, not null. + * @param searchChar The char to be searched for. + * @param start The start index, negative starts at the string start. + * @return The index where the search char was found, -1 if not found. + * @since 3.6 updated to behave more like {@link String}. */ static int indexOf(final CharSequence cs, final int searchChar, int start) { if (cs instanceof String) { @@ -110,6 +157,7 @@ static int indexOf(final CharSequence cs, final int searchChar, int start) { return i; } } + return NOT_FOUND; } //supplementary characters (LANG1300) if (searchChar <= Character.MAX_CODE_POINT) { @@ -126,51 +174,106 @@ static int indexOf(final CharSequence cs, final int searchChar, int start) { } /** - * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. + * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the {@code CharSequence} to be searched for - * @param start the start index - * @return the index where the search sequence was found + * @param cs The {@link CharSequence} to be processed. + * @param searchChar The {@link CharSequence} to find. + * @param start The start index. + * @return The index where the search sequence was found. */ - static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) { - return cs.toString().indexOf(searchChar.toString(), start); -// if (cs instanceof String && searchChar instanceof String) { -// // TODO: Do we assume searchChar is usually relatively small; -// // If so then calling toString() on it is better than reverting to -// // the green implementation in the else block -// return ((String) cs).indexOf((String) searchChar, start); -// } else { -// // TODO: Implement rather than convert to String -// return cs.toString().indexOf(searchChar.toString(), start); -// } + static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, int start) { + if (searchChar == null || cs == null) { + return NOT_FOUND; + } + if (searchChar instanceof String) { + if (cs instanceof String) { + return ((String) cs).lastIndexOf((String) searchChar, start); + } + if (cs instanceof StringBuilder) { + return ((StringBuilder) cs).lastIndexOf((String) searchChar, start); + } + if (cs instanceof StringBuffer) { + return ((StringBuffer) cs).lastIndexOf((String) searchChar, start); + } + } + + final int len1 = cs.length(); + final int len2 = searchChar.length(); + + if (start > len1) { + start = len1; + } + + if (start < 0 || len2 > len1) { + return NOT_FOUND; + } + + if (len2 == 0) { + return start; + } + + if (len2 <= TO_STRING_LIMIT) { + if (cs instanceof String) { + return ((String) cs).lastIndexOf(searchChar.toString(), start); + } + if (cs instanceof StringBuilder) { + return ((StringBuilder) cs).lastIndexOf(searchChar.toString(), start); + } + if (cs instanceof StringBuffer) { + return ((StringBuffer) cs).lastIndexOf(searchChar.toString(), start); + } + } + + if (start + len2 > len1) { + start = len1 - len2; + } + + final char char0 = searchChar.charAt(0); + + int i = start; + while (true) { + while (cs.charAt(i) != char0) { + i--; + if (i < 0) { + return NOT_FOUND; + } + } + if (checkLaterThan1(cs, searchChar, len2, i)) { + return i; + } + i--; + if (i < 0) { + return NOT_FOUND; + } + } } /** - * Returns the index within {@code cs} of the last occurrence of - * the specified character, searching backward starting at the - * specified index. For values of {@code searchChar} in the range - * from 0 to 0xFFFF (inclusive), the index returned is the largest - * value k such that: - *
    -     * (this.charAt(k) == searchChar) && (k <= start)
    -     * 
    - * is true. For other values of {@code searchChar}, it is the - * largest value k such that: - *
    -     * (this.codePointAt(k) == searchChar) && (k <= start)
    -     * 
    - * is true. In either case, if no such character occurs in {@code cs} - * at or before position {@code start}, then {@code -1} is returned. + * Returns the index within {@code cs} of the last occurrence of the specified character, searching backward starting at the specified index. For values of + * {@code searchChar} in the range from 0 to 0xFFFF (inclusive), the index returned is the largest value k such that: * - *

    All indices are specified in {@code char} values - * (Unicode code units). + *

    +     * (this.charAt(k) == searchChar) && (k <= start)
    +     * 
    + *

    + * is true. For other values of {@code searchChar}, it is the largest value k such that: + *

    * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the char to be searched for - * @param start the start index, negative returns -1, beyond length starts at end - * @return the index where the search char was found, -1 if not found - * @since 3.6 updated to behave more like {@code String} + *

    +     * (this.codePointAt(k) == searchChar) && (k <= start)
    +     * 
    + *

    + * is true. In either case, if no such character occurs in {@code cs} at or before position {@code start}, then {@code -1} is returned. + *

    + *

    + * All indices are specified in {@code char} values (Unicode code units). + *

    + * + * @param cs The {@link CharSequence} to be processed. + * @param searchChar The char to be searched for. + * @param start The start index, negative returns -1, beyond length starts at end. + * @return The index where the search char was found, -1 if not found. + * @since 3.6 updated to behave more like {@link String}. */ static int lastIndexOf(final CharSequence cs, final int searchChar, int start) { if (cs instanceof String) { @@ -189,16 +292,15 @@ static int lastIndexOf(final CharSequence cs, final int searchChar, int start) { return i; } } + return NOT_FOUND; } //supplementary characters (LANG1300) //NOTE - we must do a forward traversal for this to avoid duplicating code points if (searchChar <= Character.MAX_CODE_POINT) { final char[] chars = Character.toChars(searchChar); - //make sure it's not the last index - if (start == sz - 1) { - return NOT_FOUND; - } - for (int i = start; i >= 0; i--) { + // A supplementary code point spans two chars, so its high surrogate can start no later + // than sz - 2; clamp the search origin instead of bailing out when start is the last index. + for (int i = Math.min(start, sz - 2); i >= 0; i--) { final char high = cs.charAt(i); final char low = cs.charAt(i + 1); if (chars[0] == high && chars[1] == low) { @@ -210,97 +312,146 @@ static int lastIndexOf(final CharSequence cs, final int searchChar, int start) { } /** - * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf - * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the {@code CharSequence} to be searched for - * @param start the start index - * @return the index where the search sequence was found - */ - static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, final int start) { - return cs.toString().lastIndexOf(searchChar.toString(), start); -// if (cs instanceof String && searchChar instanceof String) { -// // TODO: Do we assume searchChar is usually relatively small; -// // If so then calling toString() on it is better than reverting to -// // the green implementation in the else block -// return ((String) cs).lastIndexOf((String) searchChar, start); -// } else { -// // TODO: Implement rather than convert to String -// return cs.toString().lastIndexOf(searchChar.toString(), start); -// } - } - - /** - * Green implementation of toCharArray. + * Tests if two string regions are equal. * - * @param cs the {@code CharSequence} to be processed - * @return the resulting char array + * @param cs The {@link CharSequence} to be processed. + * @param ignoreCase whether or not to be case-insensitive. + * @param thisStart The index to start on the {@code cs} CharSequence. + * @param substring The {@link CharSequence} to be looked for. + * @param start The index to start on the {@code substring} CharSequence. + * @param length character length of the region. + * @return whether the region matched. + * @see String#regionMatches(boolean, int, String, int, int) */ - static char[] toCharArray(final CharSequence cs) { - if (cs instanceof String) { - return ((String) cs).toCharArray(); - } - final int sz = cs.length(); - final char[] array = new char[cs.length()]; - for (int i = 0; i < sz; i++) { - array[i] = cs.charAt(i); - } - return array; - } - - /** - * Green implementation of regionMatches. - * - * @param cs the {@code CharSequence} to be processed - * @param ignoreCase whether or not to be case insensitive - * @param thisStart the index to start on the {@code cs} CharSequence - * @param substring the {@code CharSequence} to be looked for - * @param start the index to start on the {@code substring} CharSequence - * @param length character length of the region - * @return whether the region matched - */ - static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, - final CharSequence substring, final int start, final int length) { + static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, final CharSequence substring, final int start, + final int length) { + // Green implementation of regionMatches. if (cs instanceof String && substring instanceof String) { return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); } - int index1 = thisStart; - int index2 = start; - int tmpLen = length; - // Extract these first so we detect NPEs the same as the java.lang.String version final int srcLen = cs.length() - thisStart; final int otherLen = substring.length() - start; - // Check for invalid parameters if (thisStart < 0 || start < 0 || length < 0) { return false; } - // Check that the regions are long enough if (srcLen < length || otherLen < length) { return false; } - - while (tmpLen-- > 0) { - final char c1 = cs.charAt(index1++); - final char c2 = substring.charAt(index2++); - + final int end1 = thisStart + length; + final int end2 = start + length; + int index1 = thisStart; + int index2 = start; + while (index1 < end1 && index2 < end2) { + final char c1 = cs.charAt(index1); + final char c2 = substring.charAt(index2); if (c1 == c2) { + index1++; + index2++; continue; } - if (!ignoreCase) { return false; } - - // The same check as in String.regionMatches(): - if (Character.toUpperCase(c1) != Character.toUpperCase(c2) - && Character.toLowerCase(c1) != Character.toLowerCase(c2)) { - return false; + // The same case-insensitive check as String#regionMatches(boolean, int, String, int, int). + if (!equalsIgnoreCase(c1, c2)) { + // Only fold a supplementary code point split across a surrogate pair where String itself does, so + // every CharSequence type gives the same result that String does on the running JDK (see field). + if (!STRING_FOLDS_SUPPLEMENTARY_CASE) { + return false; + } + int cp1 = c1; + if (Character.isHighSurrogate(c1)) { + if (index1 + 1 < end1 && Character.isLowSurrogate(cs.charAt(index1 + 1))) { + cp1 = Character.toCodePoint(c1, cs.charAt(index1 + 1)); + index1++; + } + } else if (Character.isLowSurrogate(c1) && index1 > thisStart && Character.isHighSurrogate(cs.charAt(index1 - 1))) { + cp1 = Character.toCodePoint(cs.charAt(index1 - 1), c1); + } + int cp2 = c2; + if (Character.isHighSurrogate(c2)) { + if (index2 + 1 < end2 && Character.isLowSurrogate(substring.charAt(index2 + 1))) { + cp2 = Character.toCodePoint(c2, substring.charAt(index2 + 1)); + index2++; + } + } else if (Character.isLowSurrogate(c2) && index2 > start && Character.isHighSurrogate(substring.charAt(index2 - 1))) { + cp2 = Character.toCodePoint(substring.charAt(index2 - 1), c2); + } + if (!equalsIgnoreCase(cp1, cp2)) { + return false; + } } + index1++; + index2++; } - return true; } + + /** + * Returns a new {@link CharSequence} that is a subsequence of this + * sequence starting with the {@code char} value at the specified index. + * + *

    This provides the {@link CharSequence} equivalent to {@link String#substring(int)}. + * The length (in {@code char}) of the returned sequence is {@code length() - start}, + * so if {@code start == end} then an empty sequence is returned.

    + * + * @param cs The specified subsequence, null returns null. + * @param start The start index, inclusive, valid. + * @return A new subsequence, may be null. + * @throws IndexOutOfBoundsException if {@code start} is negative or if + * {@code start} is greater than {@code length()}. + */ + public static CharSequence subSequence(final CharSequence cs, final int start) { + return cs == null ? null : cs.subSequence(start, cs.length()); + } + + /** + * Converts the given CharSequence to a char[]. + * + * @param source The {@link CharSequence} to be processed. + * @return The resulting char array, never null. + * @since 3.11 + */ + public static char[] toCharArray(final CharSequence source) { + // See CharSequenceUtilsBenchmark + final int len = StringUtils.length(source); + if (len == 0) { + return ArrayUtils.EMPTY_CHAR_ARRAY; + } + if (source instanceof String) { + return ((String) source).toCharArray(); + } + if (source instanceof StringBuilder) { + final char[] array = new char[len]; + ((StringBuilder) source).getChars(0, len, array, 0); + return array; + } + if (source instanceof StringBuffer) { + final char[] array = new char[len]; + ((StringBuffer) source).getChars(0, len, array, 0); + return array; + } + final char[] array = new char[len]; + for (int i = 0; i < len; i++) { + array[i] = source.charAt(i); + } + return array; + } + + /** + * {@link CharSequenceUtils} instances should NOT be constructed in + * standard programming. + * + *

    This constructor is public to permit tools that require a JavaBean + * instance to operate.

    + * + * @deprecated TODO Make private in 4.0. + */ + @Deprecated + public CharSequenceUtils() { + // empty + } } diff --git a/src/main/java/org/apache/commons/lang3/CharSet.java b/src/main/java/org/apache/commons/lang3/CharSet.java index 795511553ad..b2504323cd8 100644 --- a/src/main/java/org/apache/commons/lang3/CharSet.java +++ b/src/main/java/org/apache/commons/lang3/CharSet.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,16 +19,18 @@ import java.io.Serializable; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; /** - *

    A set of characters.

    + * A set of characters. * *

    Instances are immutable, but instances of subclasses may not be.

    * *

    #ThreadSafe#

    + * * @since 1.0 */ public class CharSet implements Serializable { @@ -42,37 +44,45 @@ public class CharSet implements Serializable { /** * A CharSet defining no characters. + * * @since 2.0 */ public static final CharSet EMPTY = new CharSet((String) null); /** * A CharSet defining ASCII alphabetic characters "a-zA-Z". + * * @since 2.0 */ public static final CharSet ASCII_ALPHA = new CharSet("a-zA-Z"); /** * A CharSet defining ASCII alphabetic characters "a-z". + * * @since 2.0 */ public static final CharSet ASCII_ALPHA_LOWER = new CharSet("a-z"); /** * A CharSet defining ASCII alphabetic characters "A-Z". + * * @since 2.0 */ public static final CharSet ASCII_ALPHA_UPPER = new CharSet("A-Z"); /** * A CharSet defining ASCII alphabetic characters "0-9". + * * @since 2.0 */ public static final CharSet ASCII_NUMERIC = new CharSet("0-9"); /** * A Map of the common cases used in the factory. - * Subclasses can add more common patterns if desired + *

    + * Subclasses can add more common patterns if desired. + *

    + * * @since 2.0 */ protected static final Map COMMON = Collections.synchronizedMap(new HashMap<>()); @@ -87,12 +97,8 @@ public class CharSet implements Serializable { COMMON.put("0-9", ASCII_NUMERIC); } - /** The set of CharRange objects. */ - private final Set set = Collections.synchronizedSet(new HashSet<>()); - - //----------------------------------------------------------------------- /** - *

    Factory method to create a new CharSet using a special syntax.

    + * Creates a new CharSet using the syntax described below. * *
      *
    • {@code null} or empty string ("") @@ -109,10 +115,10 @@ public class CharSet implements Serializable { * *

      The matching order is:

      *
        - *
      1. Negated multi character range, such as "^a-e" - *
      2. Ordinary multi character range, such as "a-e" - *
      3. Negated single character, such as "^a" - *
      4. Ordinary single character, such as "a" + *
      5. Negated multi character range, such as "^a-e"
      6. + *
      7. Ordinary multi character range, such as "a-e"
      8. + *
      9. Negated single character, such as "^a"
      10. + *
      11. Ordinary single character, such as "a"
      12. *
      * *

      Matching works left to right. Once a match is found the @@ -148,13 +154,13 @@ public class CharSet implements Serializable { * *

      All CharSet objects returned by this method will be immutable.

      * - * @param setStrs Strings to merge into the set, may be null - * @return a CharSet instance + * @param setStrs Strings to merge into the set, may be null. + * @return A CharSet instance. * @since 2.4 */ public static CharSet getInstance(final String... setStrs) { if (setStrs == null) { - return null; + return EMPTY; } if (setStrs.length == 1) { final CharSet common = COMMON.get(setStrs[0]); @@ -165,24 +171,27 @@ public static CharSet getInstance(final String... setStrs) { return new CharSet(setStrs); } - //----------------------------------------------------------------------- + /** The set of CharRange objects. */ + private final Set set = Collections.synchronizedSet(new LinkedHashSet<>()); + /** - *

      Constructs a new CharSet using the set syntax. - * Each string is merged in with the set.

      + * Lock object for synchronizing access. + */ + private final Serializable lock = new SerializableObject(); + + /** + * Constructs a new CharSet using the set syntax. + * Each string is merged in with the set. * - * @param set Strings to merge into the initial set - * @throws NullPointerException if set is {@code null} + * @param set Strings to merge into the initial set. + * @throws NullPointerException if set is {@code null}. */ protected CharSet(final String... set) { - super(); - for (final String s : set) { - add(s); - } + Stream.of(set).forEach(this::add); } - //----------------------------------------------------------------------- /** - *

      Add a set definition string to the {@code CharSet}.

      + * Add a set definition string to the {@link CharSet}. * * @param str set definition string */ @@ -190,7 +199,6 @@ protected void add(final String str) { if (str == null) { return; } - final int len = str.length(); int pos = 0; while (pos < len) { @@ -215,49 +223,39 @@ protected void add(final String str) { } } - //----------------------------------------------------------------------- /** - *

      Gets the internal set as an array of CharRange objects.

      - * - * @return an array of immutable CharRange objects - * @since 2.0 - */ -// NOTE: This is no longer public as CharRange is no longer a public class. -// It may be replaced when CharSet moves to Range. - /*public*/ CharRange[] getCharRanges() { - return set.toArray(new CharRange[0]); - } - - //----------------------------------------------------------------------- - /** - *

      Does the {@code CharSet} contain the specified - * character {@code ch}.

      + * Tests whether this {@link CharSet} contain the specified character {@code ch}. + *

      + * Examples using the negation character: + *

      + *
      +     *     CharSet.getInstance("^a-c").contains('a') = false
      +     *     CharSet.getInstance("^a-c").contains('d') = true
      +     *     CharSet.getInstance("^^a-c").contains('a') = true // (only '^' is negated)
      +     *     CharSet.getInstance("^^a-c").contains('^') = false
      +     *     CharSet.getInstance("^a-cd-f").contains('d') = true
      +     *     CharSet.getInstance("a-c^").contains('^') = true
      +     *     CharSet.getInstance("^", "a-c").contains('^') = true
      +     * 
      * - * @param ch the character to check for - * @return {@code true} if the set contains the characters + * @param ch The character to check. + * @return {@code true} if the set contains the characters. */ public boolean contains(final char ch) { - synchronized(set) { - for (final CharRange range : set) { - if (range.contains(ch)) { - return true; - } - } + synchronized (lock) { + return set.stream().anyMatch(range -> range.contains(ch)); } - return false; } - // Basics - //----------------------------------------------------------------------- /** - *

      Compares two {@code CharSet} objects, returning true if they represent - * exactly the same set of characters defined in the same way.

      + * Compares two {@link CharSet} objects, returning true if they represent + * exactly the same set of characters defined in the same way. * - *

      The two sets {@code abc} and {@code a-c} are not + *

      The two sets {@code abc} and {@code a-c} are not * equal according to this method.

      * - * @param obj the object to compare to - * @return true if equal + * @param obj The object to compare. + * @return true if equal. * @since 2.0 */ @Override @@ -273,9 +271,21 @@ public boolean equals(final Object obj) { } /** - *

      Gets a hash code compatible with the equals method.

      + * Gets the set of character ranges. + *

      + * Package private for testing. + *

      + * + * @return The set of character ranges. + */ + Set getCharRanges() { + return set; + } + + /** + * Gets a hash code compatible with the equals method. * - * @return a suitable hash code + * @return A suitable hash code. * @since 2.0 */ @Override @@ -284,9 +294,9 @@ public int hashCode() { } /** - *

      Gets a string representation of the set.

      + * Gets a string representation of the set. * - * @return string representation of the set + * @return string representation of the set. */ @Override public String toString() { diff --git a/src/main/java/org/apache/commons/lang3/CharSetUtils.java b/src/main/java/org/apache/commons/lang3/CharSetUtils.java index b3580b7c25d..bab79e081c7 100644 --- a/src/main/java/org/apache/commons/lang3/CharSetUtils.java +++ b/src/main/java/org/apache/commons/lang3/CharSetUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,22 +16,25 @@ */ package org.apache.commons.lang3; +import org.apache.commons.lang3.stream.Streams; + /** - *

      Operations on {@code CharSet} instances.

      + * Operations on {@link CharSet} instances. * *

      This class handles {@code null} input gracefully. * An exception will not be thrown for a {@code null} input. - * Each method documents its behaviour in more detail.

      + * Each method documents its behavior in more detail.

      * *

      #ThreadSafe#

      + * * @see CharSet * @since 1.0 */ public class CharSetUtils { /** - *

      Takes an argument in set-syntax, see evaluateSet, - * and identifies whether any of the characters are present in the specified string.

      + * Takes an argument in set-syntax, see evaluateSet, + * and identifies whether any of the characters are present in the specified string. * *
            * CharSetUtils.containsAny(null, *)        = false
      @@ -42,14 +45,14 @@ public class CharSetUtils {
            * CharSetUtils.containsAny("hello", "a-d") = false
            * 
      * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @see CharSet#getInstance(String...) for set-syntax. * @param str String to look for characters in, may be null * @param set String[] set of characters to identify, may be null * @return whether or not the characters in the set are in the primary string * @since 3.2 */ public static boolean containsAny(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { + if (isEmpty(str, set)) { return false; } final CharSet chars = CharSet.getInstance(set); @@ -62,8 +65,8 @@ public static boolean containsAny(final String str, final String... set) { } /** - *

      Takes an argument in set-syntax, see evaluateSet, - * and returns the number of characters present in the specified string.

      + * Takes an argument in set-syntax, see evaluateSet, + * and returns the number of characters present in the specified string. * *
            * CharSetUtils.count(null, *)        = 0
      @@ -74,13 +77,13 @@ public static boolean containsAny(final String str, final String... set) {
            * CharSetUtils.count("hello", "a-e") = 1
            * 
      * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @see CharSet#getInstance(String...) for set-syntax. * @param str String to count characters in, may be null * @param set String[] set of characters to count, may be null - * @return the character count, zero if null string input + * @return The character count, zero if null string input */ public static int count(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { + if (isEmpty(str, set)) { return 0; } final CharSet chars = CharSet.getInstance(set); @@ -101,19 +104,12 @@ public static int count(final String str, final String... set) { * @return whether or not the String is empty */ private static boolean deepEmpty(final String[] strings) { - if (strings != null) { - for (final String s : strings) { - if (StringUtils.isNotEmpty(s)) { - return false; - } - } - } - return true; + return Streams.of(strings).allMatch(StringUtils::isEmpty); } /** - *

      Takes an argument in set-syntax, see evaluateSet, - * and deletes any of characters present in the specified string.

      + * Takes an argument in set-syntax, see evaluateSet, + * and deletes any of characters present in the specified string. * *
            * CharSetUtils.delete(null, *)        = null
      @@ -124,21 +120,25 @@ private static boolean deepEmpty(final String[] strings) {
            * CharSetUtils.delete("hello", "le")  = "ho"
            * 
      * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @see CharSet#getInstance(String...) for set-syntax. * @param str String to delete characters from, may be null * @param set String[] set of characters to delete, may be null - * @return the modified String, {@code null} if null string input + * @return The modified String, {@code null} if null string input */ public static String delete(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { + if (isEmpty(str, set)) { return str; } return modify(str, set, false); } + private static boolean isEmpty(final String str, final String... set) { + return StringUtils.isEmpty(str) || deepEmpty(set); + } + /** - *

      Takes an argument in set-syntax, see evaluateSet, - * and keeps any of characters present in the specified string.

      + * Takes an argument in set-syntax, see evaluateSet, + * and keeps any of characters present in the specified string. * *
            * CharSetUtils.keep(null, *)        = null
      @@ -149,10 +149,10 @@ public static String delete(final String str, final String... set) {
            * CharSetUtils.keep("hello", "le")  = "ell"
            * 
      * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @see CharSet#getInstance(String...) for set-syntax. * @param str String to keep characters from, may be null * @param set String[] set of characters to keep, may be null - * @return the modified String, {@code null} if null string input + * @return The modified String, {@code null} if null string input * @since 2.0 */ public static String keep(final String str, final String... set) { @@ -166,12 +166,12 @@ public static String keep(final String str, final String... set) { } /** - * Implementation of delete and keep + * Implements delete and keep. * * @param str String to modify characters within * @param set String[] set of characters to modify * @param expect whether to evaluate on match, or non-match - * @return the modified String, not null + * @return The modified String, not null */ private static String modify(final String str, final String[] set, final boolean expect) { final CharSet chars = CharSet.getInstance(set); @@ -186,8 +186,8 @@ private static String modify(final String str, final String[] set, final boolean } /** - *

      Squeezes any repetitions of a character that is mentioned in the - * supplied set.

      + * Squeezes any repetitions of a character that is mentioned in the + * supplied set. * *
            * CharSetUtils.squeeze(null, *)        = null
      @@ -198,13 +198,13 @@ private static String modify(final String str, final String[] set, final boolean
            * CharSetUtils.squeeze("hello", "a-e") = "hello"
            * 
      * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. - * @param str the string to squeeze, may be null - * @param set the character set to use for manipulation, may be null - * @return the modified String, {@code null} if null string input + * @see CharSet#getInstance(String...) for set-syntax. + * @param str The string to squeeze, may be null + * @param set The character set to use for manipulation, may be null + * @return The modified String, {@code null} if null string input */ public static String squeeze(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { + if (isEmpty(str, set)) { return str; } final CharSet chars = CharSet.getInstance(set); @@ -212,7 +212,7 @@ public static String squeeze(final String str, final String... set) { final char[] chrs = str.toCharArray(); final int sz = chrs.length; char lastChar = chrs[0]; - char ch = ' '; + char ch; Character inChars = null; Character notInChars = null; buffer.append(lastChar); @@ -237,13 +237,15 @@ public static String squeeze(final String str, final String... set) { } /** - *

      CharSetUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}.

      + * CharSetUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}. * *

      This constructor is public to permit tools that require a JavaBean instance * to operate.

      + * + * @deprecated TODO Make private in 4.0. */ + @Deprecated public CharSetUtils() { - super(); } } diff --git a/src/main/java/org/apache/commons/lang3/CharUtils.java b/src/main/java/org/apache/commons/lang3/CharUtils.java index 3c8ad3e6688..bb345a936d8 100644 --- a/src/main/java/org/apache/commons/lang3/CharUtils.java +++ b/src/main/java/org/apache/commons/lang3/CharUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,35 +16,38 @@ */ package org.apache.commons.lang3; +import java.util.Objects; + /** - *

      Operations on char primitives and Character objects.

      + * Operations on char primitives and Character objects. * *

      This class tries to handle {@code null} input gracefully. * An exception will not be thrown for a {@code null} input. - * Each method documents its behaviour in more detail.

      + * Each method documents its behavior in more detail.

      * *

      #ThreadSafe#

      + * * @since 2.1 */ public class CharUtils { - private static final String[] CHAR_STRING_ARRAY = new String[128]; + private static final String[] CHAR_STRING_ARRAY = ArrayUtils.setAll(new String[128], i -> String.valueOf((char) i)); private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** - * {@code \u000a} linefeed LF ('\n'). + * Linefeed character LF ({@code '\n'}, Unicode 000a). * - * @see JLF: Escape Sequences + * @see JLF: Escape Sequences * for Character and String Literals * @since 2.2 */ public static final char LF = '\n'; /** - * {@code \u000d} carriage return CR ('\r'). + * Carriage return character CR ('\r', Unicode 000d). * - * @see JLF: Escape Sequences + * @see JLF: Escape Sequences * for Character and String Literals * @since 2.2 */ @@ -57,89 +60,287 @@ public class CharUtils { */ public static final char NUL = '\0'; - static { - for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) { - CHAR_STRING_ARRAY[c] = String.valueOf(c); - } + /** + * Compares two {@code char} values numerically. This is the same functionality as provided in Java 7. + * + * @param x The first {@code char} to compare + * @param y The second {@code char} to compare + * @return The value {@code 0} if {@code x == y}; + * a value less than {@code 0} if {@code x < y}; and + * a value greater than {@code 0} if {@code x > y} + * @since 3.4 + */ + public static int compare(final char x, final char y) { + return x - y; } /** - *

      {@code CharUtils} instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code CharUtils.toString('c');}.

      + * Tests whether the character is ASCII 7 bit. * - *

      This constructor is public to permit tools that require a JavaBean instance - * to operate.

      + *
      +     *   CharUtils.isAscii('a')  = true
      +     *   CharUtils.isAscii('A')  = true
      +     *   CharUtils.isAscii('3')  = true
      +     *   CharUtils.isAscii('-')  = true
      +     *   CharUtils.isAscii('\n') = true
      +     *   CharUtils.isAscii('©') = false
      +     * 
      + * + * @param ch The character to check + * @return true if less than 128 */ - public CharUtils() { - super(); + public static boolean isAscii(final char ch) { + return ch < 128; } - //----------------------------------------------------------------------- /** - *

      Converts the character to a Character.

      + * Tests whether the character is ASCII 7 bit alphabetic. * - *

      For ASCII 7 bit characters, this uses a cache that will return the - * same Character object each time.

      + *
      +     *   CharUtils.isAsciiAlpha('a')  = true
      +     *   CharUtils.isAsciiAlpha('A')  = true
      +     *   CharUtils.isAsciiAlpha('3')  = false
      +     *   CharUtils.isAsciiAlpha('-')  = false
      +     *   CharUtils.isAsciiAlpha('\n') = false
      +     *   CharUtils.isAsciiAlpha('©') = false
      +     * 
      + * + * @param ch The character to check + * @return true if between 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlpha(final char ch) { + return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch); + } + + /** + * Tests whether the character is ASCII 7 bit alphabetic lower case. * *
      -     *   CharUtils.toCharacterObject(' ')  = ' '
      -     *   CharUtils.toCharacterObject('A')  = 'A'
      +     *   CharUtils.isAsciiAlphaLower('a')  = true
      +     *   CharUtils.isAsciiAlphaLower('A')  = false
      +     *   CharUtils.isAsciiAlphaLower('3')  = false
      +     *   CharUtils.isAsciiAlphaLower('-')  = false
      +     *   CharUtils.isAsciiAlphaLower('\n') = false
      +     *   CharUtils.isAsciiAlphaLower('©') = false
            * 
      * - * @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches chars 0 through 127. - * @param ch the character to convert - * @return a Character of the specified character + * @param ch The character to check + * @return true if between 97 and 122 inclusive */ - @Deprecated - public static Character toCharacterObject(final char ch) { - return Character.valueOf(ch); + public static boolean isAsciiAlphaLower(final char ch) { + return ch >= 'a' && ch <= 'z'; } /** - *

      Converts the String to a Character using the first character, returning - * null for empty Strings.

      + * Tests whether the character is ASCII 7 bit alphanumeric character. * - *

      For ASCII 7 bit characters, this uses a cache that will return the - * same Character object each time.

      + *
      +     *   CharUtils.isAsciiAlphanumeric('a')  = true
      +     *   CharUtils.isAsciiAlphanumeric('A')  = true
      +     *   CharUtils.isAsciiAlphanumeric('3')  = true
      +     *   CharUtils.isAsciiAlphanumeric('-')  = false
      +     *   CharUtils.isAsciiAlphanumeric('\n') = false
      +     *   CharUtils.isAsciiAlphanumeric('©') = false
      +     * 
      + * + * @param ch The character to check + * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlphanumeric(final char ch) { + return isAsciiAlpha(ch) || isAsciiNumeric(ch); + } + + /** + * Tests whether the character is ASCII 7 bit alphabetic upper case. * *
      -     *   CharUtils.toCharacterObject(null) = null
      -     *   CharUtils.toCharacterObject("")   = null
      -     *   CharUtils.toCharacterObject("A")  = 'A'
      -     *   CharUtils.toCharacterObject("BA") = 'B'
      +     *   CharUtils.isAsciiAlphaUpper('a')  = false
      +     *   CharUtils.isAsciiAlphaUpper('A')  = true
      +     *   CharUtils.isAsciiAlphaUpper('3')  = false
      +     *   CharUtils.isAsciiAlphaUpper('-')  = false
      +     *   CharUtils.isAsciiAlphaUpper('\n') = false
      +     *   CharUtils.isAsciiAlphaUpper('©') = false
            * 
      * - * @param str the character to convert - * @return the Character value of the first letter of the String + * @param ch The character to check + * @return true if between 65 and 90 inclusive */ - public static Character toCharacterObject(final String str) { - if (StringUtils.isEmpty(str)) { - return null; - } - return Character.valueOf(str.charAt(0)); + public static boolean isAsciiAlphaUpper(final char ch) { + return ch >= 'A' && ch <= 'Z'; + } + + /** + * Tests whether the character is ASCII 7 bit control. + * + *
      +     *   CharUtils.isAsciiControl('a')  = false
      +     *   CharUtils.isAsciiControl('A')  = false
      +     *   CharUtils.isAsciiControl('3')  = false
      +     *   CharUtils.isAsciiControl('-')  = false
      +     *   CharUtils.isAsciiControl('\n') = true
      +     *   CharUtils.isAsciiControl('©') = false
      +     * 
      + * + * @param ch The character to check + * @return true if less than 32 or equals 127 + */ + public static boolean isAsciiControl(final char ch) { + return ch < 32 || ch == 127; + } + + /** + * Tests whether the character is ASCII 7 bit numeric. + * + *
      +     *   CharUtils.isAsciiNumeric('a')  = false
      +     *   CharUtils.isAsciiNumeric('A')  = false
      +     *   CharUtils.isAsciiNumeric('3')  = true
      +     *   CharUtils.isAsciiNumeric('-')  = false
      +     *   CharUtils.isAsciiNumeric('\n') = false
      +     *   CharUtils.isAsciiNumeric('©') = false
      +     * 
      + * + * @param ch The character to check + * @return true if between 48 and 57 inclusive + */ + public static boolean isAsciiNumeric(final char ch) { + return ch >= '0' && ch <= '9'; + } + + /** + * Tests whether the character is ASCII 7 bit numeric. + * + *
      +     *   CharUtils.isAsciiNumeric('a')  = false
      +     *   CharUtils.isAsciiNumeric('A')  = false
      +     *   CharUtils.isAsciiNumeric('3')  = true
      +     *   CharUtils.isAsciiNumeric('-')  = false
      +     *   CharUtils.isAsciiNumeric('\n') = false
      +     *   CharUtils.isAsciiNumeric('©') = false
      +     * 
      + * + * @param ch The code point to check. + * @return true if between 48 and 57 inclusive. + * @since 3.21.0 + */ + public static boolean isAsciiNumeric(final int ch) { + return ch >= '0' && ch <= '9'; + } + + /** + * Tests whether the character is ASCII 7 bit printable. + * + *
      +     *   CharUtils.isAsciiPrintable('a')  = true
      +     *   CharUtils.isAsciiPrintable('A')  = true
      +     *   CharUtils.isAsciiPrintable('3')  = true
      +     *   CharUtils.isAsciiPrintable('-')  = true
      +     *   CharUtils.isAsciiPrintable('\n') = false
      +     *   CharUtils.isAsciiPrintable('©') = false
      +     * 
      + * + * @param ch The character to check + * @return true if between 32 and 126 inclusive + */ + public static boolean isAsciiPrintable(final char ch) { + return ch >= 32 && ch < 127; + } + + /** + * Tests whether a character is a hexadecimal character. + * + *
      +     *   CharUtils.isHex('0')  = true
      +     *   CharUtils.isHex('3')  = true
      +     *   CharUtils.isHex('9')  = true
      +     *   CharUtils.isHex('a')  = true
      +     *   CharUtils.isHex('f')  = true
      +     *   CharUtils.isHex('g')  = false
      +     *   CharUtils.isHex('A')  = true
      +     *   CharUtils.isHex('F')  = true
      +     *   CharUtils.isHex('G')  = false
      +     *   CharUtils.isHex('#')  = false
      +     *   CharUtils.isHex('-')  = false
      +     *   CharUtils.isHex('\n') = false
      +     *   CharUtils.isHex('©') = false
      +     * 
      + * + * @param ch The character to test. + * @return true if character is a hexadecimal character. + * @since 3.18.0 + */ + public static boolean isHex(final char ch) { + return isAsciiNumeric(ch) || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F'; + } + + /** + * Tests whether a character is a hexadecimal character. + * + *
      +     *   CharUtils.isHex('0')  = true
      +     *   CharUtils.isHex('3')  = true
      +     *   CharUtils.isHex('9')  = true
      +     *   CharUtils.isHex('a')  = true
      +     *   CharUtils.isHex('f')  = true
      +     *   CharUtils.isHex('g')  = false
      +     *   CharUtils.isHex('A')  = true
      +     *   CharUtils.isHex('F')  = true
      +     *   CharUtils.isHex('G')  = false
      +     *   CharUtils.isHex('#')  = false
      +     *   CharUtils.isHex('-')  = false
      +     *   CharUtils.isHex('\n') = false
      +     *   CharUtils.isHex('©') = false
      +     * 
      + * + * @param ch The code point to test. + * @return true if character is a hexadecimal character. + * @since 3.21.0 + */ + public static boolean isHex(final int ch) { + return isAsciiNumeric(ch) || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F'; + } + + /** + * Tests if the given char is an octal digit. Octal digits are the character representations of the digits 0 to 7. + * + * @param ch The byte to check. + * @return true if the given char is the character representation of one of the digits from 0 to 7. + * @since 3.21.0 + */ + public static boolean isOctal(final byte ch) { + return ch >= '0' && ch <= '7'; + } + + /** + * Tests if the given char is an octal digit. Octal digits are the character representations of the digits 0 to 7. + * + * @param ch The char to check. + * @return true if the given char is the character representation of one of the digits from 0 to 7. + * @since 3.18.0 + */ + public static boolean isOctal(final char ch) { + return ch >= '0' && ch <= '7'; } - //----------------------------------------------------------------------- /** - *

      Converts the Character to a char throwing an exception for {@code null}.

      + * Converts the Character to a char throwing an exception for {@code null}. * *
            *   CharUtils.toChar(' ')  = ' '
            *   CharUtils.toChar('A')  = 'A'
      -     *   CharUtils.toChar(null) throws IllegalArgumentException
      +     *   CharUtils.toChar(null) throws NullPointerException
            * 
      * - * @param ch the character to convert - * @return the char value of the Character - * @throws IllegalArgumentException if the Character is null + * @param ch The character to convert + * @return The char value of the Character + * @throws NullPointerException if the Character is null */ public static char toChar(final Character ch) { - Validate.isTrue(ch != null, "The Character must not be null"); - return ch.charValue(); + return Objects.requireNonNull(ch, "ch").charValue(); } /** - *

      Converts the Character to a char handling {@code null}.

      + * Converts the Character to a char handling {@code null}. * *
            *   CharUtils.toChar(null, 'X') = 'X'
      @@ -147,41 +348,38 @@ public static char toChar(final Character ch) {
            *   CharUtils.toChar('A', 'X')  = 'A'
            * 
      * - * @param ch the character to convert - * @param defaultValue the value to use if the Character is null - * @return the char value of the Character or the default if null + * @param ch The character to convert + * @param defaultValue The value to use if the Character is null + * @return The char value of the Character or the default if null */ public static char toChar(final Character ch, final char defaultValue) { - if (ch == null) { - return defaultValue; - } - return ch.charValue(); + return ch != null ? ch.charValue() : defaultValue; } - //----------------------------------------------------------------------- /** - *

      Converts the String to a char using the first character, throwing - * an exception on empty Strings.

      + * Converts the String to a char using the first character, throwing + * an exception on empty Strings. * *
            *   CharUtils.toChar("A")  = 'A'
            *   CharUtils.toChar("BA") = 'B'
      -     *   CharUtils.toChar(null) throws IllegalArgumentException
      +     *   CharUtils.toChar(null) throws NullPointerException
            *   CharUtils.toChar("")   throws IllegalArgumentException
            * 
      * - * @param str the character to convert - * @return the char value of the first letter of the String + * @param str The character to convert + * @return The char value of the first letter of the String + * @throws NullPointerException if the string is null * @throws IllegalArgumentException if the String is empty */ public static char toChar(final String str) { - Validate.isTrue(StringUtils.isNotEmpty(str), "The String must not be empty"); + Validate.notEmpty(str, "The String must not be empty"); return str.charAt(0); } /** - *

      Converts the String to a char using the first character, defaulting - * the value on empty Strings.

      + * Converts the String to a char using the first character, defaulting + * the value on empty Strings. * *
            *   CharUtils.toChar(null, 'X') = 'X'
      @@ -190,21 +388,50 @@ public static char toChar(final String str) {
            *   CharUtils.toChar("BA", 'X') = 'B'
            * 
      * - * @param str the character to convert - * @param defaultValue the value to use if the Character is null - * @return the char value of the first letter of the String or the default if null + * @param str The character to convert + * @param defaultValue The value to use if the Character is null + * @return The char value of the first letter of the String or the default if null */ public static char toChar(final String str, final char defaultValue) { - if (StringUtils.isEmpty(str)) { - return defaultValue; - } - return str.charAt(0); + return StringUtils.isEmpty(str) ? defaultValue : str.charAt(0); + } + + /** + * Delegates to {@link Character#valueOf(char)}. + * + * @param c The character to convert + * @return A {@code Character} representing {@code c}. + * @deprecated Use {@link Character#valueOf(char)}. + */ + @Deprecated + public static Character toCharacterObject(final char c) { + return Character.valueOf(c); + } + + /** + * Converts the String to a Character using the first character, returning + * null for empty Strings. + * + *

      For ASCII 7 bit characters, this uses a cache that will return the + * same Character object each time.

      + * + *
      +     *   CharUtils.toCharacterObject(null) = null
      +     *   CharUtils.toCharacterObject("")   = null
      +     *   CharUtils.toCharacterObject("A")  = 'A'
      +     *   CharUtils.toCharacterObject("BA") = 'B'
      +     * 
      + * + * @param str The character to convert + * @return The Character value of the first letter of the String + */ + public static Character toCharacterObject(final String str) { + return StringUtils.isEmpty(str) ? null : Character.valueOf(str.charAt(0)); } - //----------------------------------------------------------------------- /** - *

      Converts the character to the Integer it represents, throwing an - * exception if the character is not numeric.

      + * Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric. * *

      This method converts the char '1' to the int 1 and so on.

      * @@ -213,8 +440,8 @@ public static char toChar(final String str, final char defaultValue) { * CharUtils.toIntValue('A') throws IllegalArgumentException * * - * @param ch the character to convert - * @return the int value of the character + * @param ch The character to convert + * @return The int value of the character * @throws IllegalArgumentException if the character is not ASCII numeric */ public static int toIntValue(final char ch) { @@ -225,8 +452,8 @@ public static int toIntValue(final char ch) { } /** - *

      Converts the character to the Integer it represents, throwing an - * exception if the character is not numeric.

      + * Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric. * *

      This method converts the char '1' to the int 1 and so on.

      * @@ -235,41 +462,38 @@ public static int toIntValue(final char ch) { * CharUtils.toIntValue('A', -1) = -1 * * - * @param ch the character to convert - * @param defaultValue the default value to use if the character is not numeric - * @return the int value of the character + * @param ch The character to convert + * @param defaultValue The default value to use if the character is not numeric + * @return The int value of the character */ public static int toIntValue(final char ch, final int defaultValue) { - if (!isAsciiNumeric(ch)) { - return defaultValue; - } - return ch - 48; + return isAsciiNumeric(ch) ? ch - 48 : defaultValue; } /** - *

      Converts the character to the Integer it represents, throwing an - * exception if the character is not numeric.

      + * Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric. * *

      This method converts the char '1' to the int 1 and so on.

      * *
            *   CharUtils.toIntValue('3')  = 3
      -     *   CharUtils.toIntValue(null) throws IllegalArgumentException
      +     *   CharUtils.toIntValue(null) throws NullPointerException
            *   CharUtils.toIntValue('A')  throws IllegalArgumentException
            * 
      * - * @param ch the character to convert, not null - * @return the int value of the character - * @throws IllegalArgumentException if the Character is not ASCII numeric or is null + * @param ch The character to convert, not null + * @return The int value of the character + * @throws NullPointerException if the Character is null + * @throws IllegalArgumentException if the Character is not ASCII numeric */ public static int toIntValue(final Character ch) { - Validate.isTrue(ch != null, "The character must not be null"); - return toIntValue(ch.charValue()); + return toIntValue(toChar(ch)); } /** - *

      Converts the character to the Integer it represents, throwing an - * exception if the character is not numeric.

      + * Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric. * *

      This method converts the char '1' to the int 1 and so on.

      * @@ -279,20 +503,16 @@ public static int toIntValue(final Character ch) { * CharUtils.toIntValue('A', -1) = -1 * * - * @param ch the character to convert - * @param defaultValue the default value to use if the character is not numeric - * @return the int value of the character + * @param ch The character to convert + * @param defaultValue The default value to use if the character is not numeric + * @return The int value of the character */ public static int toIntValue(final Character ch, final int defaultValue) { - if (ch == null) { - return defaultValue; - } - return toIntValue(ch.charValue(), defaultValue); + return ch != null ? toIntValue(ch.charValue(), defaultValue) : defaultValue; } - //----------------------------------------------------------------------- /** - *

      Converts the character to a String that contains the one character.

      + * Converts the character to a String that contains the one character. * *

      For ASCII 7 bit characters, this uses a cache that will return the * same String object each time.

      @@ -302,18 +522,18 @@ public static int toIntValue(final Character ch, final int defaultValue) { * CharUtils.toString('A') = "A" * * - * @param ch the character to convert - * @return a String containing the one specified character + * @param ch The character to convert + * @return A String containing the one specified character */ public static String toString(final char ch) { - if (ch < 128) { + if (ch < CHAR_STRING_ARRAY.length) { return CHAR_STRING_ARRAY[ch]; } - return new String(new char[] {ch}); + return String.valueOf(ch); } /** - *

      Converts the character to a String that contains the one character.

      + * Converts the character to a String that contains the one character. * *

      For ASCII 7 bit characters, this uses a cache that will return the * same String object each time.

      @@ -326,19 +546,15 @@ public static String toString(final char ch) { * CharUtils.toString('A') = "A" * * - * @param ch the character to convert - * @return a String containing the one specified character + * @param ch The character to convert + * @return A String containing the one specified character */ public static String toString(final Character ch) { - if (ch == null) { - return null; - } - return toString(ch.charValue()); + return ch != null ? toString(ch.charValue()) : null; } - //-------------------------------------------------------------------------- /** - *

      Converts the string to the Unicode format '\u0020'.

      + * Converts the string to the Unicode format '\u0020'. * *

      This format is the Java source code format.

      * @@ -347,19 +563,19 @@ public static String toString(final Character ch) { * CharUtils.unicodeEscaped('A') = "\u0041" * * - * @param ch the character to convert - * @return the escaped Unicode string + * @param ch The character to convert + * @return The escaped Unicode string */ public static String unicodeEscaped(final char ch) { return "\\u" + - HEX_DIGITS[(ch >> 12) & 15] + - HEX_DIGITS[(ch >> 8) & 15] + - HEX_DIGITS[(ch >> 4) & 15] + - HEX_DIGITS[(ch) & 15]; + HEX_DIGITS[ch >> 12 & 15] + + HEX_DIGITS[ch >> 8 & 15] + + HEX_DIGITS[ch >> 4 & 15] + + HEX_DIGITS[ch & 15]; } /** - *

      Converts the string to the Unicode format '\u0020'.

      + * Converts the string to the Unicode format '\u0020'. * *

      This format is the Java source code format.

      * @@ -371,180 +587,24 @@ public static String unicodeEscaped(final char ch) { * CharUtils.unicodeEscaped('A') = "\u0041" * * - * @param ch the character to convert, may be null - * @return the escaped Unicode string, null if null input + * @param ch The character to convert, may be null + * @return The escaped Unicode string, null if null input */ public static String unicodeEscaped(final Character ch) { - if (ch == null) { - return null; - } - return unicodeEscaped(ch.charValue()); - } - - //-------------------------------------------------------------------------- - /** - *

      Checks whether the character is ASCII 7 bit.

      - * - *
      -     *   CharUtils.isAscii('a')  = true
      -     *   CharUtils.isAscii('A')  = true
      -     *   CharUtils.isAscii('3')  = true
      -     *   CharUtils.isAscii('-')  = true
      -     *   CharUtils.isAscii('\n') = true
      -     *   CharUtils.isAscii('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if less than 128 - */ - public static boolean isAscii(final char ch) { - return ch < 128; - } - - /** - *

      Checks whether the character is ASCII 7 bit printable.

      - * - *
      -     *   CharUtils.isAsciiPrintable('a')  = true
      -     *   CharUtils.isAsciiPrintable('A')  = true
      -     *   CharUtils.isAsciiPrintable('3')  = true
      -     *   CharUtils.isAsciiPrintable('-')  = true
      -     *   CharUtils.isAsciiPrintable('\n') = false
      -     *   CharUtils.isAsciiPrintable('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if between 32 and 126 inclusive - */ - public static boolean isAsciiPrintable(final char ch) { - return ch >= 32 && ch < 127; - } - - /** - *

      Checks whether the character is ASCII 7 bit control.

      - * - *
      -     *   CharUtils.isAsciiControl('a')  = false
      -     *   CharUtils.isAsciiControl('A')  = false
      -     *   CharUtils.isAsciiControl('3')  = false
      -     *   CharUtils.isAsciiControl('-')  = false
      -     *   CharUtils.isAsciiControl('\n') = true
      -     *   CharUtils.isAsciiControl('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if less than 32 or equals 127 - */ - public static boolean isAsciiControl(final char ch) { - return ch < 32 || ch == 127; - } - - /** - *

      Checks whether the character is ASCII 7 bit alphabetic.

      - * - *
      -     *   CharUtils.isAsciiAlpha('a')  = true
      -     *   CharUtils.isAsciiAlpha('A')  = true
      -     *   CharUtils.isAsciiAlpha('3')  = false
      -     *   CharUtils.isAsciiAlpha('-')  = false
      -     *   CharUtils.isAsciiAlpha('\n') = false
      -     *   CharUtils.isAsciiAlpha('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if between 65 and 90 or 97 and 122 inclusive - */ - public static boolean isAsciiAlpha(final char ch) { - return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch); - } - - /** - *

      Checks whether the character is ASCII 7 bit alphabetic upper case.

      - * - *
      -     *   CharUtils.isAsciiAlphaUpper('a')  = false
      -     *   CharUtils.isAsciiAlphaUpper('A')  = true
      -     *   CharUtils.isAsciiAlphaUpper('3')  = false
      -     *   CharUtils.isAsciiAlphaUpper('-')  = false
      -     *   CharUtils.isAsciiAlphaUpper('\n') = false
      -     *   CharUtils.isAsciiAlphaUpper('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if between 65 and 90 inclusive - */ - public static boolean isAsciiAlphaUpper(final char ch) { - return ch >= 'A' && ch <= 'Z'; - } - - /** - *

      Checks whether the character is ASCII 7 bit alphabetic lower case.

      - * - *
      -     *   CharUtils.isAsciiAlphaLower('a')  = true
      -     *   CharUtils.isAsciiAlphaLower('A')  = false
      -     *   CharUtils.isAsciiAlphaLower('3')  = false
      -     *   CharUtils.isAsciiAlphaLower('-')  = false
      -     *   CharUtils.isAsciiAlphaLower('\n') = false
      -     *   CharUtils.isAsciiAlphaLower('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if between 97 and 122 inclusive - */ - public static boolean isAsciiAlphaLower(final char ch) { - return ch >= 'a' && ch <= 'z'; - } - - /** - *

      Checks whether the character is ASCII 7 bit numeric.

      - * - *
      -     *   CharUtils.isAsciiNumeric('a')  = false
      -     *   CharUtils.isAsciiNumeric('A')  = false
      -     *   CharUtils.isAsciiNumeric('3')  = true
      -     *   CharUtils.isAsciiNumeric('-')  = false
      -     *   CharUtils.isAsciiNumeric('\n') = false
      -     *   CharUtils.isAsciiNumeric('©') = false
      -     * 
      - * - * @param ch the character to check - * @return true if between 48 and 57 inclusive - */ - public static boolean isAsciiNumeric(final char ch) { - return ch >= '0' && ch <= '9'; + return ch != null ? unicodeEscaped(ch.charValue()) : null; } /** - *

      Checks whether the character is ASCII 7 bit numeric.

      - * - *
      -     *   CharUtils.isAsciiAlphanumeric('a')  = true
      -     *   CharUtils.isAsciiAlphanumeric('A')  = true
      -     *   CharUtils.isAsciiAlphanumeric('3')  = true
      -     *   CharUtils.isAsciiAlphanumeric('-')  = false
      -     *   CharUtils.isAsciiAlphanumeric('\n') = false
      -     *   CharUtils.isAsciiAlphanumeric('©') = false
      -     * 
      + * {@link CharUtils} instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code CharUtils.toString('c');}. * - * @param ch the character to check - * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive - */ - public static boolean isAsciiAlphanumeric(final char ch) { - return isAsciiAlpha(ch) || isAsciiNumeric(ch); - } - - /** - *

      Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.

      + *

      This constructor is public to permit tools that require a JavaBean instance + * to operate.

      * - * @param x the first {@code char} to compare - * @param y the second {@code char} to compare - * @return the value {@code 0} if {@code x == y}; - * a value less than {@code 0} if {@code x < y}; and - * a value greater than {@code 0} if {@code x > y} - * @since 3.4 + * @deprecated TODO Make private in 4.0. */ - public static int compare(final char x, final char y) { - return x-y; + @Deprecated + public CharUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/Charsets.java b/src/main/java/org/apache/commons/lang3/Charsets.java index c80bb90a377..7f7c2ebe45d 100644 --- a/src/main/java/org/apache/commons/lang3/Charsets.java +++ b/src/main/java/org/apache/commons/lang3/Charsets.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -32,13 +32,13 @@ * * @since 3.10 */ -class Charsets { +final class Charsets { /** * Returns the given {@code charset} or the default Charset if {@code charset} is null. * - * @param charset a Charset or null. - * @return the given {@code charset} or the default Charset if {@code charset} is null. + * @param charset A Charset or null. + * @return The given {@code charset} or the default Charset if {@code charset} is null. */ static Charset toCharset(final Charset charset) { return charset == null ? Charset.defaultCharset() : charset; @@ -47,8 +47,8 @@ static Charset toCharset(final Charset charset) { /** * Returns the given {@code charset} or the default Charset if {@code charset} is null. * - * @param charsetName a Charset or null. - * @return the given {@code charset} or the default Charset if {@code charset} is null. + * @param charsetName A Charset or null. + * @return The given {@code charset} or the default Charset if {@code charset} is null. * @throws UnsupportedCharsetException If no support for the named charset is available in this instance of the Java * virtual machine */ @@ -59,8 +59,8 @@ static Charset toCharset(final String charsetName) { /** * Returns the given {@code charset} or the default Charset if {@code charset} is null. * - * @param charsetName a Charset or null. - * @return the given {@code charset} or the default Charset if {@code charset} is null. + * @param charsetName A Charset or null. + * @return The given {@code charset} or the default Charset if {@code charset} is null. */ static String toCharsetName(final String charsetName) { return charsetName == null ? Charset.defaultCharset().name() : charsetName; diff --git a/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java b/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java index 95cdd2aabab..a970508dad1 100644 --- a/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,8 +17,10 @@ package org.apache.commons.lang3; +import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; +import java.util.Objects; /** * Helps work with {@link ClassLoader}. @@ -27,27 +29,62 @@ */ public class ClassLoaderUtils { + private static final URL[] EMPTY_URL_ARRAY = {}; + + /** + * Gets the system class loader's URLs, if any. + * + * @return The system class loader's URLs, if any. + * @since 3.13.0 + */ + public static URL[] getSystemURLs() { + return getURLs(ClassLoader.getSystemClassLoader()); + } + + /** + * Gets the current thread's context class loader's URLs, if any. + * + * @return The current thread's context class loader's URLs, if any. + * @since 3.13.0 + */ + public static URL[] getThreadURLs() { + return getURLs(Thread.currentThread().getContextClassLoader()); + } + + private static URL[] getURLs(final ClassLoader cl) { + return cl instanceof URLClassLoader ? ((URLClassLoader) cl).getURLs() : EMPTY_URL_ARRAY; + } + /** * Converts the given class loader to a String calling {@link #toString(URLClassLoader)}. * * @param classLoader to URLClassLoader to convert. - * @return the formated string. + * @return The formatted string. */ public static String toString(final ClassLoader classLoader) { if (classLoader instanceof URLClassLoader) { return toString((URLClassLoader) classLoader); } - return classLoader.toString(); + return Objects.toString(classLoader); } /** - * Converts the given URLClassLoader to a String in the format - * {@code "URLClassLoader.toString() + [URL1, URL2, ...]"}. + * Converts the given URLClassLoader to a String in the format {@code "URLClassLoader.toString() + [URL1, URL2, ...]"}. * * @param classLoader to URLClassLoader to convert. - * @return the formated string. + * @return The formatted string. */ public static String toString(final URLClassLoader classLoader) { - return classLoader + Arrays.toString(classLoader.getURLs()); + return classLoader != null ? classLoader + Arrays.toString(classLoader.getURLs()) : "null"; + } + + /** + * Make private in 4.0. + * + * @deprecated TODO Make private in 4.0. + */ + @Deprecated + public ClassLoaderUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/ClassPathUtils.java b/src/main/java/org/apache/commons/lang3/ClassPathUtils.java index df3773a344c..4956ce08eec 100644 --- a/src/main/java/org/apache/commons/lang3/ClassPathUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassPathUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,10 +16,14 @@ */ package org.apache.commons.lang3; +import java.util.Objects; + /** * Operations regarding the classpath. * - *

      The methods of this class do not allow {@code null} inputs.

      + *

      + * The methods of this class do not allow {@code null} inputs. + *

      * * @since 3.3 */ @@ -27,104 +31,130 @@ public class ClassPathUtils { /** - *

      {@code ClassPathUtils} instances should NOT be constructed in - * standard programming. Instead, the class should be used as - * {@code ClassPathUtils.toFullyQualifiedName(MyClass.class, "MyClass.properties");}.

      + * Converts a package name to a Java path ('/'). * - *

      This constructor is public to permit tools that require a JavaBean - * instance to operate.

      + * @param path The source path. + * @return A package name. + * @throws NullPointerException if {@code path} is null. + * @since 3.13.0 */ - public ClassPathUtils() { - super(); + public static String packageToPath(final String path) { + return Objects.requireNonNull(path, "path").replace('.', '/'); + } + + /** + * Converts a Java path ('/') to a package name. + * + * @param path The source path. + * @return A package name. + * @throws NullPointerException if {@code path} is null. + * @since 3.13.0 + */ + public static String pathToPackage(final String path) { + return Objects.requireNonNull(path, "path").replace('/', '.'); } /** * Returns the fully qualified name for the resource with name {@code resourceName} relative to the given context. * - *

      Note that this method does not check whether the resource actually exists. - * It only constructs the name. - * Null inputs are not allowed.

      + *

      + * Note that this method does not check whether the resource actually exists. It only constructs the name. Null inputs are not allowed. + *

      * *
            * ClassPathUtils.toFullyQualifiedName(StringUtils.class, "StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
            * 
      * - * @param context The context for constructing the name. - * @param resourceName the resource name to construct the fully qualified name for. - * @return the fully qualified name of the resource with name {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or {@code resourceName} is null. + * @param context The context for constructing the name. + * @param resourceName The resource name to construct the fully qualified name for. + * @return The fully qualified name of the resource with name {@code resourceName}. + * @throws NullPointerException if either {@code context} or {@code resourceName} is null. */ public static String toFullyQualifiedName(final Class context, final String resourceName) { - Validate.notNull(context, "Parameter '%s' must not be null!", "context" ); - Validate.notNull(resourceName, "Parameter '%s' must not be null!", "resourceName"); + Objects.requireNonNull(context, "context"); + Objects.requireNonNull(resourceName, "resourceName"); return toFullyQualifiedName(context.getPackage(), resourceName); } /** * Returns the fully qualified name for the resource with name {@code resourceName} relative to the given context. * - *

      Note that this method does not check whether the resource actually exists. - * It only constructs the name. - * Null inputs are not allowed.

      + *

      + * Note that this method does not check whether the resource actually exists. It only constructs the name. Null inputs are not allowed. + *

      * *
            * ClassPathUtils.toFullyQualifiedName(StringUtils.class.getPackage(), "StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
            * 
      * - * @param context The context for constructing the name. - * @param resourceName the resource name to construct the fully qualified name for. - * @return the fully qualified name of the resource with name {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or {@code resourceName} is null. + * @param context The context for constructing the name. + * @param resourceName The resource name to construct the fully qualified name for. + * @return The fully qualified name of the resource with name {@code resourceName}. + * @throws NullPointerException if either {@code context} or {@code resourceName} is null. */ public static String toFullyQualifiedName(final Package context, final String resourceName) { - Validate.notNull(context, "Parameter '%s' must not be null!", "context" ); - Validate.notNull(resourceName, "Parameter '%s' must not be null!", "resourceName"); + Objects.requireNonNull(context, "context"); + Objects.requireNonNull(resourceName, "resourceName"); return context.getName() + "." + resourceName; } /** * Returns the fully qualified path for the resource with name {@code resourceName} relative to the given context. * - *

      Note that this method does not check whether the resource actually exists. - * It only constructs the path. - * Null inputs are not allowed.

      + *

      + * Note that this method does not check whether the resource actually exists. It only constructs the path. Null inputs are not allowed. + *

      * *
            * ClassPathUtils.toFullyQualifiedPath(StringUtils.class, "StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
            * 
      * - * @param context The context for constructing the path. - * @param resourceName the resource name to construct the fully qualified path for. - * @return the fully qualified path of the resource with name {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or {@code resourceName} is null. + * @param context The context for constructing the path. + * @param resourceName The resource name to construct the fully qualified path for. + * @return The fully qualified path of the resource with name {@code resourceName}. + * @throws NullPointerException if either {@code context} or {@code resourceName} is null. */ public static String toFullyQualifiedPath(final Class context, final String resourceName) { - Validate.notNull(context, "Parameter '%s' must not be null!", "context" ); - Validate.notNull(resourceName, "Parameter '%s' must not be null!", "resourceName"); + Objects.requireNonNull(context, "context"); + Objects.requireNonNull(resourceName, "resourceName"); return toFullyQualifiedPath(context.getPackage(), resourceName); } - /** * Returns the fully qualified path for the resource with name {@code resourceName} relative to the given context. * - *

      Note that this method does not check whether the resource actually exists. - * It only constructs the path. - * Null inputs are not allowed.

      + *

      + * Note that this method does not check whether the resource actually exists. It only constructs the path. Null inputs are not allowed. + *

      * *
            * ClassPathUtils.toFullyQualifiedPath(StringUtils.class.getPackage(), "StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
            * 
      * - * @param context The context for constructing the path. - * @param resourceName the resource name to construct the fully qualified path for. - * @return the fully qualified path of the resource with name {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or {@code resourceName} is null. + * @param context The context for constructing the path. + * @param resourceName The resource name to construct the fully qualified path for. + * @return The fully qualified path of the resource with name {@code resourceName}. + * @throws NullPointerException if either {@code context} or {@code resourceName} is null. */ public static String toFullyQualifiedPath(final Package context, final String resourceName) { - Validate.notNull(context, "Parameter '%s' must not be null!", "context" ); - Validate.notNull(resourceName, "Parameter '%s' must not be null!", "resourceName"); - return context.getName().replace('.', '/') + "/" + resourceName; + Objects.requireNonNull(context, "context"); + Objects.requireNonNull(resourceName, "resourceName"); + return packageToPath(context.getName()) + "/" + resourceName; + } + + /** + * {@link ClassPathUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as + * {@code ClassPathUtils.toFullyQualifiedName(MyClass.class, "MyClass.properties");}. + * + *

      + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

      + * + * @deprecated TODO Make private in 4.0. + */ + @Deprecated + public ClassPathUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index 4b90ab2973e..302d6269c54 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -18,41 +18,82 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; - -import org.apache.commons.lang3.mutable.MutableObject; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; +import java.util.stream.Collectors; /** - *

      Operates on classes without using reflection.

      + * Operates on classes without using reflection. * - *

      This class handles invalid {@code null} inputs as best it can. - * Each method documents its behaviour in more detail.

      + *

      + * This class handles invalid {@code null} inputs as best it can. Each method documents its behavior in more detail. + *

      * - *

      The notion of a {@code canonical name} includes the human - * readable name for the type, for example {@code int[]}. The - * non-canonical method variants work with the JVM names, such as - * {@code [I}.

      + *

      + * The notion of a {@code canonical name} includes the human-readable name for the type, for example {@code int[]}. The + * non-canonical method variants work with the JVM names, such as {@code [I}. + *

      * * @since 2.0 */ public class ClassUtils { /** - * Inclusivity literals for {@link #hierarchy(Class, Interfaces)}. + * Enumerates inclusivity literals for {@link #hierarchy(Class, Interfaces)}. + * * @since 3.2 */ public enum Interfaces { - INCLUDE, EXCLUDE + + /** Includes interfaces. */ + INCLUDE, + + /** Excludes interfaces. */ + EXCLUDE } + /** + * The JLS-specified maximum class name length {@value}. + * + * @see Class#forName(String, boolean, ClassLoader) + * @see JVM: Array dimension limits in JVM Specification CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary + */ + private static final int MAX_CLASS_NAME_LENGTH = 65535; + + /** + * The JVM-specified {@code CONSTANT_Class_info} structure defines an array type descriptor is valid only if it represents {@value} or fewer dimensions. + * + * @see Class#forName(String, boolean, ClassLoader) + * @see JVM: Array dimension limits in JVM Specification CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary + */ + private static final int MAX_JVM_ARRAY_DIMENSION = 255; + + /** + * The maximum number of array dimensions. + */ + private static final int MAX_DIMENSIONS = 255; + + private static final Pattern ARRAY_TAIL_PATTERN = Pattern.compile("(?:\\[\\])+"); + + private static final Comparator> COMPARATOR = (o1, o2) -> Objects.compare(getName(o1), getName(o2), String::compareTo); + /** * The package separator character: {@code '.' == {@value}}. */ @@ -74,411 +115,217 @@ public enum Interfaces { public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); /** - * Maps names of primitives to their corresponding primitive {@code Class}es. + * Maps names of primitives to their corresponding primitive {@link Class}es. */ - private static final Map> namePrimitiveMap = new HashMap<>(); + private static final Map> NAME_PRIMITIVE_MAP = new HashMap<>(); + static { - namePrimitiveMap.put("boolean", Boolean.TYPE); - namePrimitiveMap.put("byte", Byte.TYPE); - namePrimitiveMap.put("char", Character.TYPE); - namePrimitiveMap.put("short", Short.TYPE); - namePrimitiveMap.put("int", Integer.TYPE); - namePrimitiveMap.put("long", Long.TYPE); - namePrimitiveMap.put("double", Double.TYPE); - namePrimitiveMap.put("float", Float.TYPE); - namePrimitiveMap.put("void", Void.TYPE); + NAME_PRIMITIVE_MAP.put(Boolean.TYPE.getName(), Boolean.TYPE); + NAME_PRIMITIVE_MAP.put(Byte.TYPE.getName(), Byte.TYPE); + NAME_PRIMITIVE_MAP.put(Character.TYPE.getName(), Character.TYPE); + NAME_PRIMITIVE_MAP.put(Double.TYPE.getName(), Double.TYPE); + NAME_PRIMITIVE_MAP.put(Float.TYPE.getName(), Float.TYPE); + NAME_PRIMITIVE_MAP.put(Integer.TYPE.getName(), Integer.TYPE); + NAME_PRIMITIVE_MAP.put(Long.TYPE.getName(), Long.TYPE); + NAME_PRIMITIVE_MAP.put(Short.TYPE.getName(), Short.TYPE); + NAME_PRIMITIVE_MAP.put(Void.TYPE.getName(), Void.TYPE); } /** - * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}. + * Maps primitive {@link Class}es to their corresponding wrapper {@link Class}. */ - private static final Map, Class> primitiveWrapperMap = new HashMap<>(); + private static final Map, Class> PRIMITIVE_WRAPPER_MAP = new HashMap<>(); + static { - primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); - primitiveWrapperMap.put(Byte.TYPE, Byte.class); - primitiveWrapperMap.put(Character.TYPE, Character.class); - primitiveWrapperMap.put(Short.TYPE, Short.class); - primitiveWrapperMap.put(Integer.TYPE, Integer.class); - primitiveWrapperMap.put(Long.TYPE, Long.class); - primitiveWrapperMap.put(Double.TYPE, Double.class); - primitiveWrapperMap.put(Float.TYPE, Float.class); - primitiveWrapperMap.put(Void.TYPE, Void.TYPE); + PRIMITIVE_WRAPPER_MAP.put(Boolean.TYPE, Boolean.class); + PRIMITIVE_WRAPPER_MAP.put(Byte.TYPE, Byte.class); + PRIMITIVE_WRAPPER_MAP.put(Character.TYPE, Character.class); + PRIMITIVE_WRAPPER_MAP.put(Short.TYPE, Short.class); + PRIMITIVE_WRAPPER_MAP.put(Integer.TYPE, Integer.class); + PRIMITIVE_WRAPPER_MAP.put(Long.TYPE, Long.class); + PRIMITIVE_WRAPPER_MAP.put(Double.TYPE, Double.class); + PRIMITIVE_WRAPPER_MAP.put(Float.TYPE, Float.class); + PRIMITIVE_WRAPPER_MAP.put(Void.TYPE, Void.TYPE); } /** - * Maps wrapper {@code Class}es to their corresponding primitive types. + * Maps wrapper {@link Class}es to their corresponding primitive types. */ - private static final Map, Class> wrapperPrimitiveMap = new HashMap<>(); + private static final Map, Class> WRAPPER_PRIMITIVE_MAP = new HashMap<>(); + static { - for (final Map.Entry, Class> entry : primitiveWrapperMap.entrySet()) { - final Class primitiveClass = entry.getKey(); - final Class wrapperClass = entry.getValue(); + PRIMITIVE_WRAPPER_MAP.forEach((primitiveClass, wrapperClass) -> { if (!primitiveClass.equals(wrapperClass)) { - wrapperPrimitiveMap.put(wrapperClass, primitiveClass); + WRAPPER_PRIMITIVE_MAP.put(wrapperClass, primitiveClass); } - } + }); } /** * Maps a primitive class name to its corresponding abbreviation used in array class names. */ - private static final Map abbreviationMap; + private static final Map ABBREVIATION_MAP; /** * Maps an abbreviation used in array class names to corresponding primitive class name. */ - private static final Map reverseAbbreviationMap; - // Feed abbreviation maps - static { - final Map m = new HashMap<>(); - m.put("int", "I"); - m.put("boolean", "Z"); - m.put("float", "F"); - m.put("long", "J"); - m.put("short", "S"); - m.put("byte", "B"); - m.put("double", "D"); - m.put("char", "C"); - final Map r = new HashMap<>(); - for (final Map.Entry e : m.entrySet()) { - r.put(e.getValue(), e.getKey()); - } - abbreviationMap = Collections.unmodifiableMap(m); - reverseAbbreviationMap = Collections.unmodifiableMap(r); - } + private static final Map REVERSE_ABBREVIATION_MAP; - /** - *

      ClassUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as - * {@code ClassUtils.getShortClassName(cls)}.

      - * - *

      This constructor is public to permit tools that require a JavaBean - * instance to operate.

      - */ - public ClassUtils() { - super(); + /** Feed abbreviation maps. */ + static { + final Map map = new HashMap<>(); + map.put(Integer.TYPE.getName(), "I"); + map.put(Boolean.TYPE.getName(), "Z"); + map.put(Float.TYPE.getName(), "F"); + map.put(Long.TYPE.getName(), "J"); + map.put(Short.TYPE.getName(), "S"); + map.put(Byte.TYPE.getName(), "B"); + map.put(Double.TYPE.getName(), "D"); + map.put(Character.TYPE.getName(), "C"); + ABBREVIATION_MAP = Collections.unmodifiableMap(map); + REVERSE_ABBREVIATION_MAP = Collections.unmodifiableMap(map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))); } - // Short class name - // ---------------------------------------------------------------------- /** - *

      Gets the class name of the {@code object} without the package name or names.

      - * - *

      The method looks up the class of the object and then converts the name of the class invoking - * {@link #getShortClassName(Class)} (see relevant notes there).

      + * Gets the class comparator, comparing by class name. * - * @param object the class to get the short name for, may be {@code null} - * @param valueIfNull the value to return if the object is {@code null} - * @return the class name of the object without the package name, or {@code valueIfNull} - * if the argument {@code object} is {@code null} + * @return The class comparator. + * @since 3.13.0 */ - public static String getShortClassName(final Object object, final String valueIfNull) { - if (object == null) { - return valueIfNull; - } - return getShortClassName(object.getClass()); + public static Comparator> comparator() { + return COMPARATOR; } /** - *

      Gets the class name minus the package name from a {@code Class}.

      + * Given a {@link List} of {@link Class} objects, this method converts them into class names. * - *

      This method simply gets the name using {@code Class.getName()} and then calls - * {@link #getShortClassName(Class)}. See relevant notes there.

      + *

      + * A new {@link List} is returned. {@code null} objects will be copied into the returned list as {@code null}. + *

      * - * @param cls the class to get the short name for. - * @return the class name without the package name or an empty string. If the class - * is an inner class then the returned value will contain the outer class - * or classes separated with {@code .} (dot) character. + * @param classes The classes to change. + * @return A {@link List} of class names corresponding to the Class objects, {@code null} if null input. + * @throws ClassCastException if {@code classes} contains a non-{@link Class} entry. */ - public static String getShortClassName(final Class cls) { - if (cls == null) { - return StringUtils.EMPTY; - } - return getShortClassName(cls.getName()); + public static List convertClassesToClassNames(final List> classes) { + return classes == null ? null : classes.stream().map(e -> getName(e, null)).collect(Collectors.toList()); } /** - *

      Gets the class name minus the package name from a String.

      - * - *

      The string passed in is assumed to be a class name - it is not checked. The string has to be formatted the way - * as the JDK method {@code Class.getName()} returns it, and not the usual way as we write it, for example in import - * statements, or as it is formatted by {@code Class.getCanonicalName()}.

      - * - *

      The difference is is significant only in case of classes that are inner classes of some other - * classes. In this case the separator between the outer and inner class (possibly on multiple hierarchy level) has - * to be {@code $} (dollar sign) and not {@code .} (dot), as it is returned by {@code Class.getName()}

      - * - *

      Note that this method is called from the {@link #getShortClassName(Class)} method using the string - * returned by {@code Class.getName()}.

      + * Given a {@link List} of class names, this method converts them into classes. * - *

      Note that this method differs from {@link #getSimpleName(Class)} in that this will - * return, for example {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply - * return {@code "Entry"}. In this example the argument {@code className} is the string - * {@code java.util.Map$Entry} (note the {@code $} sign.

      + *

      + * A new {@link List} is returned. If the class name cannot be found, {@code null} is stored in the {@link List}. If the + * class name in the {@link List} is {@code null}, {@code null} is stored in the output {@link List}. + *

      * - * @param className the className to get the short name for. It has to be formatted as returned by - * {@code Class.getName()} and not {@code Class.getCanonicalName()} - * @return the class name of the class without the package name or an empty string. If the class is - * an inner class then value contains the outer class or classes and the separator is replaced - * to be {@code .} (dot) character. + * @param classNames The classNames to change. + * @return A {@link List} of Class objects corresponding to the class names, {@code null} if null input. + * @throws ClassCastException if classNames contains a non String entry. */ - public static String getShortClassName(String className) { - if (StringUtils.isEmpty(className)) { - return StringUtils.EMPTY; + public static List> convertClassNamesToClasses(final List classNames) { + if (classNames == null) { + return null; } - - final StringBuilder arrayPrefix = new StringBuilder(); - - // Handle array encoding - if (className.startsWith("[")) { - while (className.charAt(0) == '[') { - className = className.substring(1); - arrayPrefix.append("[]"); - } - // Strip Object type encoding - if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { - className = className.substring(1, className.length() - 1); - } - - if (reverseAbbreviationMap.containsKey(className)) { - className = reverseAbbreviationMap.get(className); + final List> classes = new ArrayList<>(classNames.size()); + classNames.forEach(className -> { + try { + classes.add(Class.forName(className)); + } catch (final Exception ex) { + classes.add(null); } - } - - final int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); - final int innerIdx = className.indexOf( - INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); - String out = className.substring(lastDotIdx + 1); - if (innerIdx != -1) { - out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); - } - return out + arrayPrefix; - } - - /** - *

      Null-safe version of {@code cls.getSimpleName()}

      - * - * @param cls the class for which to get the simple name; may be null - * @return the simple class name or the empty string in case the argument is {@code null} - * @since 3.0 - * @see Class#getSimpleName() - */ - public static String getSimpleName(final Class cls) { - return getSimpleName(cls, StringUtils.EMPTY); - } - - /** - *

      Null-safe version of {@code cls.getSimpleName()}

      - * - * @param cls the class for which to get the simple name; may be null - * @param valueIfNull the value to return if null - * @return the simple class name or {@code valueIfNull} if the - * argument {@code cls} is {@code null} - * @since 3.0 - * @see Class#getSimpleName() - */ - public static String getSimpleName(final Class cls, String valueIfNull) { - return cls == null ? valueIfNull : cls.getSimpleName(); - } - - /** - *

      Null-safe version of {@code object.getClass().getSimpleName()}

      - * - *

      It is to note that this method is overloaded and in case the argument {@code object} is a - * {@code Class} object then the {@link #getSimpleName(Class)} will be invoked. If this is - * a significant possibility then the caller should check this case and call {@code - * getSimpleName(Class.class)} or just simply use the string literal {@code "Class"}, which - * is the result of the method in that case.

      - * - * @param object the object for which to get the simple class name; may be null - * @return the simple class name or the empty string in case the argument is {@code null} - * @since 3.7 - * @see Class#getSimpleName() - */ - public static String getSimpleName(final Object object) { - return getSimpleName(object, StringUtils.EMPTY); - } - - /** - *

      Null-safe version of {@code object.getClass().getSimpleName()}

      - * - * @param object the object for which to get the simple class name; may be null - * @param valueIfNull the value to return if {@code object} is {@code null} - * @return the simple class name or {@code valueIfNull} if the - * argument {@code object} is {@code null} - * @since 3.0 - * @see Class#getSimpleName() - */ - public static String getSimpleName(final Object object, final String valueIfNull) { - return object == null ? valueIfNull : object.getClass().getSimpleName(); - } - - /** - *

      Null-safe version of {@code cls.getName()}

      - * - * @param cls the class for which to get the class name; may be null - * @return the class name or the empty string in case the argument is {@code null} - * @since 3.7 - * @see Class#getSimpleName() - */ - public static String getName(final Class cls) { - return getName(cls, StringUtils.EMPTY); - } - - /** - *

      Null-safe version of {@code cls.getName()}

      - * - * @param cls the class for which to get the class name; may be null - * @param valueIfNull the return value if the argument {@code cls} is {@code null} - * @return the class name or {@code valueIfNull} - * @since 3.7 - * @see Class#getName() - */ - public static String getName(final Class cls, final String valueIfNull) { - return cls == null ? valueIfNull : cls.getName(); - } - - /** - *

      Null-safe version of {@code object.getClass().getName()}

      - * - * @param object the object for which to get the class name; may be null - * @return the class name or the empty String - * @since 3.7 - * @see Class#getSimpleName() - */ - public static String getName(final Object object) { - return getName(object, StringUtils.EMPTY); - } - - /** - *

      Null-safe version of {@code object.getClass().getSimpleName()}

      - * - * @param object the object for which to get the class name; may be null - * @param valueIfNull the value to return if {@code object} is {@code null} - * @return the class name or {@code valueIfNull} - * @since 3.0 - * @see Class#getName() - */ - public static String getName(final Object object, final String valueIfNull) { - return object == null ? valueIfNull : object.getClass().getName(); - } - - // Package name - // ---------------------------------------------------------------------- - /** - *

      Gets the package name of an {@code Object}.

      - * - * @param object the class to get the package name for, may be null - * @param valueIfNull the value to return if null - * @return the package name of the object, or the null value - */ - public static String getPackageName(final Object object, final String valueIfNull) { - if (object == null) { - return valueIfNull; - } - return getPackageName(object.getClass()); - } - - /** - *

      Gets the package name of a {@code Class}.

      - * - * @param cls the class to get the package name for, may be {@code null}. - * @return the package name or an empty string - */ - public static String getPackageName(final Class cls) { - if (cls == null) { - return StringUtils.EMPTY; - } - return getPackageName(cls.getName()); - } - - /** - *

      Gets the package name from a {@code String}.

      - * - *

      The string passed in is assumed to be a class name - it is not checked.

      - *

      If the class is unpackaged, return an empty string.

      - * - * @param className the className to get the package name for, may be {@code null} - * @return the package name or an empty string - */ - public static String getPackageName(String className) { - if (StringUtils.isEmpty(className)) { - return StringUtils.EMPTY; - } - - // Strip array encoding - while (className.charAt(0) == '[') { - className = className.substring(1); - } - // Strip Object type encoding - if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { - className = className.substring(1); - } - - final int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); - if (i == -1) { - return StringUtils.EMPTY; - } - return className.substring(0, i); + }); + return classes; } - // Abbreviated name - // ---------------------------------------------------------------------- /** - *

      Gets the abbreviated name of a {@code Class}.

      + * Gets the abbreviated name of a {@link Class}. * - * @param cls the class to get the abbreviated name for, may be {@code null} - * @param lengthHint the desired length of the abbreviated name - * @return the abbreviated name or an empty string - * @throws IllegalArgumentException if len <= 0 + * @param cls The class to get the abbreviated name for, may be {@code null}. + * @param lengthHint The desired length of the abbreviated name. + * @return The abbreviated name or an empty string. + * @throws IllegalArgumentException if len <= 0. * @see #getAbbreviatedName(String, int) * @since 3.4 */ public static String getAbbreviatedName(final Class cls, final int lengthHint) { - if (cls == null) { - return StringUtils.EMPTY; - } - return getAbbreviatedName(cls.getName(), lengthHint); + if (cls == null) { + return StringUtils.EMPTY; + } + return getAbbreviatedName(cls.getName(), lengthHint); } /** - *

      Gets the abbreviated class name from a {@code String}.

      + * Gets the abbreviated class name from a {@link String}. * - *

      The string passed in is assumed to be a class name - it is not checked.

      + *

      + * The string passed in is assumed to be a class name - it is not checked. + *

      * - *

      The abbreviation algorithm will shorten the class name, usually without - * significant loss of meaning.

      + *

      + * The abbreviation algorithm will shorten the class name, usually without significant loss of meaning. + *

      * - *

      The abbreviated class name will always include the complete package hierarchy. - * If enough space is available, rightmost sub-packages will be displayed in full - * length. The abbreviated package names will be shortened to a single character.

      - *

      Only package names are shortened, the class simple name remains untouched. (See examples.)

      - *

      The result will be longer than the desired length only if all the package names - * shortened to a single character plus the class simple name with the separating dots - * together are longer than the desired length. In other words, when the class name - * cannot be shortened to the desired length.

      - *

      If the class name can be shortened then - * the final length will be at most {@code lengthHint} characters.

      - *

      If the {@code lengthHint} is zero or negative then the method - * throws exception. If you want to achieve the shortest possible version then - * use {@code 1} as a {@code lengthHint}.

      + *

      + * The abbreviated class name will always include the complete package hierarchy. If enough space is available, + * rightmost sub-packages will be displayed in full length. The abbreviated package names will be shortened to a single + * character. + *

      + *

      + * Only package names are shortened, the class simple name remains untouched. (See examples.) + *

      + *

      + * The result will be longer than the desired length only if all the package names shortened to a single character plus + * the class simple name with the separating dots together are longer than the desired length. In other words, when the + * class name cannot be shortened to the desired length. + *

      + *

      + * If the class name can be shortened then the final length will be at most {@code lengthHint} characters. + *

      + *

      + * If the {@code lengthHint} is zero or negative then the method throws exception. If you want to achieve the shortest + * possible version then use {@code 1} as a {@code lengthHint}. + *

      * * * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * *
      Examples
      classNamelenreturn
      null 1""
      "java.lang.String" 5"j.l.String"
      "java.lang.String"15"j.lang.String"
      "java.lang.String"30"java.lang.String"
      "org.apache.commons.lang3.ClassUtils"18"o.a.c.l.ClassUtils"
      classNamelenreturn
      null1""
      "java.lang.String"5"j.l.String"
      "java.lang.String"15"j.lang.String"
      "java.lang.String"30"java.lang.String"
      "org.apache.commons.lang3.ClassUtils"18"o.a.c.l.ClassUtils"
      * - * @param className the className to get the abbreviated name for, may be {@code null} - * @param lengthHint the desired length of the abbreviated name - * @return the abbreviated name or an empty string if the specified - * class name is {@code null} or empty string. The abbreviated name may be - * longer than the desired length if it cannot be abbreviated to the desired length. - * @throws IllegalArgumentException if {@code len <= 0} + * @param className The className to get the abbreviated name for, may be {@code null}. + * @param lengthHint The desired length of the abbreviated name. + * @return The abbreviated name or an empty string if the specified class name is {@code null} or empty string. The + * abbreviated name may be longer than the desired length if it cannot be abbreviated to the desired length. + * @throws IllegalArgumentException if {@code len <= 0}. * @since 3.4 */ public static String getAbbreviatedName(final String className, final int lengthHint) { @@ -502,8 +349,7 @@ public static String getAbbreviatedName(final String className, final int length } ++target; - if (useFull(runAheadTarget, source, abbreviated.length, lengthHint) - || target > runAheadTarget) { + if (useFull(runAheadTarget, source, abbreviated.length, lengthHint) || target > runAheadTarget) { target = runAheadTarget; } @@ -516,1046 +362,1513 @@ public static String getAbbreviatedName(final String className, final int length } /** - *

      Decides if the part that was just copied to its destination - * location in the work array can be kept as it was copied or must be - * abbreviated. It must be kept when the part is the last one, which - * is the simple name of the class. In this case the {@code source} - * index, from where the characters are copied points one position - * after the last character, a.k.a. {@code source == - * originalLength}

      - * - *

      If the part is not the last one then it can be kept - * unabridged if the number of the characters copied so far plus - * the character that are to be copied is less than or equal to the - * desired length.

      - * - * @param runAheadTarget the target index (where the characters were - * copied to) pointing after the last character - * copied when the current part was copied - * @param source the source index (where the characters were - * copied from) pointing after the last - * character copied when the current part was - * copied - * @param originalLength the original length of the class full name, - * which is abbreviated - * @param desiredLength the desired length of the abbreviated class - * name - * @return {@code true} if it can be kept in its original length - * {@code false} if the current part has to be abbreviated and - */ - private static boolean useFull(final int runAheadTarget, - final int source, - final int originalLength, - final int desiredLength) { - return source >= originalLength || - runAheadTarget + originalLength - source <= desiredLength; - } - - // Superclasses/Superinterfaces - // ---------------------------------------------------------------------- - /** - *

      Gets a {@code List} of superclasses for the given class.

      - * - * @param cls the class to look up, may be {@code null} - * @return the {@code List} of superclasses in order going up from this one - * {@code null} if null input - */ - public static List> getAllSuperclasses(final Class cls) { - if (cls == null) { - return null; - } - final List> classes = new ArrayList<>(); - Class superclass = cls.getSuperclass(); - while (superclass != null) { - classes.add(superclass); - superclass = superclass.getSuperclass(); - } - return classes; - } - - /** - *

      Gets a {@code List} of all interfaces implemented by the given - * class and its superclasses.

      + * Gets a {@link List} of all interfaces implemented by the given class and its superclasses. * - *

      The order is determined by looking through each interface in turn as - * declared in the source file and following its hierarchy up. Then each - * superclass is considered in the same way. Later duplicates are ignored, - * so the order is maintained.

      + *

      + * The order is determined by looking through each interface in turn as declared in the source file and following its + * hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order is + * maintained. + *

      * - * @param cls the class to look up, may be {@code null} - * @return the {@code List} of interfaces in order, - * {@code null} if null input + * @param cls The class to look up, may be {@code null}. + * @return The {@link List} of interfaces in order, {@code null} if null input. */ public static List> getAllInterfaces(final Class cls) { if (cls == null) { return null; } - final LinkedHashSet> interfacesFound = new LinkedHashSet<>(); getAllInterfaces(cls, interfacesFound); - return new ArrayList<>(interfacesFound); } /** - * Get the interfaces for the specified class. + * Gets the interfaces for the specified class. * - * @param cls the class to look up, may be {@code null} - * @param interfacesFound the {@code Set} of interfaces for the class + * @param cls The class to look up, may be {@code null}. + * @param interfacesFound The {@link Set} of interfaces for the class. */ - private static void getAllInterfaces(Class cls, final HashSet> interfacesFound) { + private static void getAllInterfaces(Class cls, final Set> interfacesFound) { while (cls != null) { - final Class[] interfaces = cls.getInterfaces(); - - for (final Class i : interfaces) { + for (final Class i : cls.getInterfaces()) { if (interfacesFound.add(i)) { getAllInterfaces(i, interfacesFound); } } - cls = cls.getSuperclass(); - } - } + } + } - // Convert list - // ---------------------------------------------------------------------- /** - *

      Given a {@code List} of class names, this method converts them into classes.

      + * Gets a {@link List} of superclasses for the given class. * - *

      A new {@code List} is returned. If the class name cannot be found, {@code null} - * is stored in the {@code List}. If the class name in the {@code List} is - * {@code null}, {@code null} is stored in the output {@code List}.

      + *
        + *
      1. The first entry is the superclass of the given class.
      2. + *
      3. The last entry is {@link Object}'s class.
      4. + *
      * - * @param classNames the classNames to change - * @return a {@code List} of Class objects corresponding to the class names, - * {@code null} if null input - * @throws ClassCastException if classNames contains a non String entry + * @param cls The class to look up, may be {@code null}. + * @return The {@link List} of superclasses in order going up from this one {@code null} if null input. */ - public static List> convertClassNamesToClasses(final List classNames) { - if (classNames == null) { + public static List> getAllSuperclasses(final Class cls) { + if (cls == null) { return null; } - final List> classes = new ArrayList<>(classNames.size()); - for (final String className : classNames) { - try { - classes.add(Class.forName(className)); - } catch (final Exception ex) { - classes.add(null); - } + final List> classes = new ArrayList<>(); + Class superclass = cls.getSuperclass(); + while (superclass != null) { + classes.add(superclass); + superclass = superclass.getSuperclass(); } return classes; } /** - *

      Given a {@code List} of {@code Class} objects, this method converts - * them into class names.

      - * - *

      A new {@code List} is returned. {@code null} objects will be copied into - * the returned list as {@code null}.

      + * Gets the canonical class name for a {@link Class}. * - * @param classes the classes to change - * @return a {@code List} of class names corresponding to the Class objects, - * {@code null} if null input - * @throws ClassCastException if {@code classes} contains a non-{@code Class} entry + * @param cls The class for which to get the canonical class name; may be null. + * @return The canonical name of the class, or the empty String. + * @since 3.7 + * @see Class#getCanonicalName() */ - public static List convertClassesToClassNames(final List> classes) { - if (classes == null) { - return null; - } - final List classNames = new ArrayList<>(classes.size()); - for (final Class cls : classes) { - if (cls == null) { - classNames.add(null); - } else { - classNames.add(cls.getName()); - } - } - return classNames; + public static String getCanonicalName(final Class cls) { + return getCanonicalName(cls, StringUtils.EMPTY); } - // Is assignable - // ---------------------------------------------------------------------- /** - *

      Checks if an array of Classes can be assigned to another array of Classes.

      - * - *

      This method calls {@link #isAssignable(Class, Class) isAssignable} for each - * Class pair in the input arrays. It can be used to check if a set of arguments - * (the first parameter) are suitably compatible with a set of method parameter types - * (the second parameter).

      - * - *

      Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this - * method takes into account widenings of primitive classes and - * {@code null}s.

      - * - *

      Primitive widenings allow an int to be assigned to a {@code long}, - * {@code float} or {@code double}. This method returns the correct - * result for these cases.

      - * - *

      {@code Null} may be assigned to any reference type. This method will - * return {@code true} if {@code null} is passed in and the toClass is - * non-primitive.

      - * - *

      Specifically, this method tests whether the type represented by the - * specified {@code Class} parameter can be converted to the type - * represented by this {@code Class} object via an identity conversion - * widening primitive or widening reference conversion. See - * The Java Language Specification, - * sections 5.1.1, 5.1.2 and 5.1.4 for details.

      + * Gets the canonical name for a {@link Class}. * - *

      Since Lang 3.0, this method will default behavior for - * calculating assignability between primitive and wrapper types corresponding - * to the running Java version; i.e. autoboxing will be the default - * behavior in VMs running Java versions > 1.5.

      - * - * @param classArray the array of Classes to check, may be {@code null} - * @param toClassArray the array of Classes to try to assign into, may be {@code null} - * @return {@code true} if assignment possible + * @param cls The class for which to get the canonical class name; may be null. + * @param valueIfNull The return value if null. + * @return The canonical name of the class, or {@code valueIfNull}. + * @since 3.7 + * @see Class#getCanonicalName() */ - public static boolean isAssignable(final Class[] classArray, final Class... toClassArray) { - return isAssignable(classArray, toClassArray, true); + public static String getCanonicalName(final Class cls, final String valueIfNull) { + if (cls == null) { + return valueIfNull; + } + final String canonicalName = cls.getCanonicalName(); + return canonicalName == null ? valueIfNull : canonicalName; } /** - *

      Checks if an array of Classes can be assigned to another array of Classes.

      + * Gets the canonical name for an {@link Object}. * - *

      This method calls {@link #isAssignable(Class, Class) isAssignable} for each - * Class pair in the input arrays. It can be used to check if a set of arguments - * (the first parameter) are suitably compatible with a set of method parameter types - * (the second parameter).

      - * - *

      Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this - * method takes into account widenings of primitive classes and - * {@code null}s.

      + * @param object The object for which to get the canonical class name; may be null. + * @return The canonical name of the object, or the empty String. + * @since 3.7 + * @see Class#getCanonicalName() + */ + public static String getCanonicalName(final Object object) { + return getCanonicalName(object, StringUtils.EMPTY); + } + + /** + * Gets the canonical name for an {@link Object}. * - *

      Primitive widenings allow an int to be assigned to a {@code long}, - * {@code float} or {@code double}. This method returns the correct - * result for these cases.

      + * @param object The object for which to get the canonical class name; may be null. + * @param valueIfNull The return value if null. + * @return The canonical name of the object or {@code valueIfNull}. + * @since 3.7 + * @see Class#getCanonicalName() + */ + public static String getCanonicalName(final Object object, final String valueIfNull) { + if (object == null) { + return valueIfNull; + } + final String canonicalName = object.getClass().getCanonicalName(); + return canonicalName == null ? valueIfNull : canonicalName; + } + + /** + * Converts a given name of class into canonical format. If name of class is not a name of array class it returns + * unchanged name. * - *

      {@code Null} may be assigned to any reference type. This method will - * return {@code true} if {@code null} is passed in and the toClass is - * non-primitive.

      + *

      + * The method does not change the {@code $} separators in case the class is inner class. + *

      * - *

      Specifically, this method tests whether the type represented by the - * specified {@code Class} parameter can be converted to the type - * represented by this {@code Class} object via an identity conversion - * widening primitive or widening reference conversion. See - * The Java Language Specification, - * sections 5.1.1, 5.1.2 and 5.1.4 for details.

      + *

      + * Example: + *

        + *
      • {@code getCanonicalName("[I") = "int[]"}
      • + *
      • {@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}
      • + *
      • {@code getCanonicalName("java.lang.String") = "java.lang.String"}
      • + *
      + *

      * - * @param classArray the array of Classes to check, may be {@code null} - * @param toClassArray the array of Classes to try to assign into, may be {@code null} - * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers - * @return {@code true} if assignment possible + * @param name The name of class. + * @return canonical form of class name. + * @throws IllegalArgumentException if the class name is invalid. */ - public static boolean isAssignable(Class[] classArray, Class[] toClassArray, final boolean autoboxing) { - if (!ArrayUtils.isSameLength(classArray, toClassArray)) { - return false; + private static String getCanonicalName(final String name) { + String className = StringUtils.deleteWhitespace(name); + if (className == null) { + return null; + } + int dim = 0; + final int len = className.length(); + while (dim < len && className.charAt(dim) == '[') { + dim++; + if (dim > MAX_DIMENSIONS) { + throw new IllegalArgumentException(String.format("Maximum array dimension %d exceeded", MAX_DIMENSIONS)); + } } - if (classArray == null) { - classArray = ArrayUtils.EMPTY_CLASS_ARRAY; + if (dim >= len) { + throw new IllegalArgumentException(String.format("Invalid class name %s", name)); } - if (toClassArray == null) { - toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY; + if (dim < 1) { + return className; } - for (int i = 0; i < classArray.length; i++) { - if (!isAssignable(classArray[i], toClassArray[i], autoboxing)) { - return false; + className = className.substring(dim); + if (className.startsWith("L")) { + if (!className.endsWith(";") || className.length() < 3) { + throw new IllegalArgumentException(String.format("Invalid class name %s", name)); } + className = className.substring(1, className.length() - 1); + } else if (className.length() == 1) { + final String primitive = REVERSE_ABBREVIATION_MAP.get(className.substring(0, 1)); + if (primitive == null) { + throw new IllegalArgumentException(String.format("Invalid class name %s", name)); + } + className = primitive; + } else { + throw new IllegalArgumentException(String.format("Invalid class name %s", name)); } - return true; - } - - /** - * Returns whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, - * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). - * - * @param type - * The class to query or null. - * @return true if the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, - * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). - * @since 3.1 - */ - public static boolean isPrimitiveOrWrapper(final Class type) { - if (type == null) { - return false; + final StringBuilder canonicalClassNameBuffer = new StringBuilder(className.length() + dim * 2); + canonicalClassNameBuffer.append(className); + for (int i = 0; i < dim; i++) { + canonicalClassNameBuffer.append("[]"); } - return type.isPrimitive() || isPrimitiveWrapper(type); + return canonicalClassNameBuffer.toString(); } /** - * Returns whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short}, - * {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * Gets the (initialized) class represented by {@code className} using the {@code classLoader}. This implementation + * supports the syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + *

      + * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not + * been collapsed. + *

      + *

      + * Security note: because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'} + * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating + * an untrusted class name by string comparison before calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a + * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name after + * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no + * whitespace normalization. + *

      * - * @param type - * The class to query or null. - * @return true if the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short}, - * {@link Integer}, {@link Long}, {@link Double}, {@link Float}). - * @since 3.1 + * @param classLoader The class loader to use to load the class. + * @param className The class name. + * @return The class represented by {@code className} using the {@code classLoader}. + * @throws NullPointerException if the className is null. + * @throws ClassNotFoundException if the class is not found. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see Class#forName(String, boolean, ClassLoader) + * @see JVM: Array dimension limits in JVM Specification CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary */ - public static boolean isPrimitiveWrapper(final Class type) { - return wrapperPrimitiveMap.containsKey(type); + public static Class getClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException { + return getClass(classLoader, className, true); } /** - *

      Checks if one {@code Class} can be assigned to a variable of - * another {@code Class}.

      - * - *

      Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, - * this method takes into account widenings of primitive classes and - * {@code null}s.

      - * - *

      Primitive widenings allow an int to be assigned to a long, float or - * double. This method returns the correct result for these cases.

      - * - *

      {@code Null} may be assigned to any reference type. This method - * will return {@code true} if {@code null} is passed in and the - * toClass is non-primitive.

      - * - *

      Specifically, this method tests whether the type represented by the - * specified {@code Class} parameter can be converted to the type - * represented by this {@code Class} object via an identity conversion - * widening primitive or widening reference conversion. See - * The Java Language Specification, - * sections 5.1.1, 5.1.2 and 5.1.4 for details.

      - * - *

      Since Lang 3.0, this method will default behavior for - * calculating assignability between primitive and wrapper types corresponding - * to the running Java version; i.e. autoboxing will be the default - * behavior in VMs running Java versions > 1.5.

      + * Gets the class represented by {@code className} using the {@code classLoader}. This implementation supports the + * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", and + * "{@code [Ljava.util.Map$Entry;}". + *

      + * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not + * been collapsed. + *

      + *

      + * Security note: because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'} + * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating + * an untrusted class name by string comparison before calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a + * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name after + * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no + * whitespace normalization. + *

      * - * @param cls the Class to check, may be null - * @param toClass the Class to try to assign into, returns false if null - * @return {@code true} if assignment possible - */ - public static boolean isAssignable(final Class cls, final Class toClass) { - return isAssignable(cls, toClass, true); + * @param classLoader The class loader to use to load the class. + * @param className The class name. + * @param initialize whether the class must be initialized. + * @return The class represented by {@code className} using the {@code classLoader}. + * @throws NullPointerException if the className is null. + * @throws ClassNotFoundException if the class is not found. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see Class#forName(String, boolean, ClassLoader) + * @see JVM: Array dimension limits in JVM Specification CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary + */ + public static Class getClass(final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException { + return getClass(classLoader, className, initialize, true); } /** - *

      Checks if one {@code Class} can be assigned to a variable of - * another {@code Class}.

      - * - *

      Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, - * this method takes into account widenings of primitive classes and - * {@code null}s.

      - * - *

      Primitive widenings allow an int to be assigned to a long, float or - * double. This method returns the correct result for these cases.

      + * Implements {@link #getClass(ClassLoader, String, boolean)} and {@link #getClassStrict(ClassLoader, String, boolean)}. * - *

      {@code Null} may be assigned to any reference type. This method - * will return {@code true} if {@code null} is passed in and the - * toClass is non-primitive.

      - * - *

      Specifically, this method tests whether the type represented by the - * specified {@code Class} parameter can be converted to the type - * represented by this {@code Class} object via an identity conversion - * widening primitive or widening reference conversion. See - * The Java Language Specification, - * sections 5.1.1, 5.1.2 and 5.1.4 for details.

      - * - * @param cls the Class to check, may be null - * @param toClass the Class to try to assign into, returns false if null - * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers - * @return {@code true} if assignment possible + * @param classLoader The class loader to use to load the class. + * @param className The class name. + * @param initialize whether the class must be initialized. + * @param normalizeWhitespace whether to delete all whitespace from the class name before resolving it. + * @return The class represented by {@code className} using the {@code classLoader}. + * @throws NullPointerException if the className is null. + * @throws ClassNotFoundException if the class is not found. */ - public static boolean isAssignable(Class cls, final Class toClass, final boolean autoboxing) { - if (toClass == null) { - return false; - } - // have to check for null, as isAssignableFrom doesn't - if (cls == null) { - return !toClass.isPrimitive(); - } - //autoboxing: - if (autoboxing) { - if (cls.isPrimitive() && !toClass.isPrimitive()) { - cls = primitiveToWrapper(cls); - if (cls == null) { - return false; - } - } - if (toClass.isPrimitive() && !cls.isPrimitive()) { - cls = wrapperToPrimitive(cls); - if (cls == null) { - return false; + private static Class getClass(final ClassLoader classLoader, final String className, final boolean initialize, final boolean normalizeWhitespace) + throws ClassNotFoundException { + // This method was re-written to avoid recursion and stack overflows found by fuzz testing. + String next = className; + int lastDotIndex = -1; + do { + try { + final Class clazz = getPrimitiveClass(next); + return clazz != null ? clazz : Class.forName(normalizeWhitespace ? toCleanName(next) : toEncodedName(next), initialize, classLoader); + } catch (final ClassNotFoundException ex) { + lastDotIndex = next.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + if (lastDotIndex != -1) { + next = next.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + next.substring(lastDotIndex + 1); } } - } - if (cls.equals(toClass)) { - return true; - } - if (cls.isPrimitive()) { - if (!toClass.isPrimitive()) { - return false; - } - if (Integer.TYPE.equals(cls)) { - return Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) - || Double.TYPE.equals(toClass); - } - if (Long.TYPE.equals(cls)) { - return Float.TYPE.equals(toClass) - || Double.TYPE.equals(toClass); - } - if (Boolean.TYPE.equals(cls)) { - return false; - } - if (Double.TYPE.equals(cls)) { - return false; - } - if (Float.TYPE.equals(cls)) { - return Double.TYPE.equals(toClass); - } - if (Character.TYPE.equals(cls)) { - return Integer.TYPE.equals(toClass) - || Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) - || Double.TYPE.equals(toClass); - } - if (Short.TYPE.equals(cls)) { - return Integer.TYPE.equals(toClass) - || Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) - || Double.TYPE.equals(toClass); - } - if (Byte.TYPE.equals(cls)) { - return Short.TYPE.equals(toClass) - || Integer.TYPE.equals(toClass) - || Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) - || Double.TYPE.equals(toClass); - } - // should never get here - return false; - } - return toClass.isAssignableFrom(cls); + } while (lastDotIndex != -1); + throw new ClassNotFoundException(className); } /** - *

      Converts the specified primitive Class object to its corresponding - * wrapper Class object.

      - * - *

      NOTE: From v2.2, this method handles {@code Void.TYPE}, - * returning {@code Void.TYPE}.

      + * Gets the (initialized) class represented by {@code className} using the current thread's context class loader. + * This implementation supports the syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + *

      + * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not + * been collapsed. + *

      + *

      + * Security note: because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'} + * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating + * an untrusted class name by string comparison before calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a + * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name after + * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no + * whitespace normalization. + *

      * - * @param cls the class to convert, may be null - * @return the wrapper class for {@code cls} or {@code cls} if - * {@code cls} is not a primitive. {@code null} if null input. - * @since 2.1 + * @param className The class name + * @return The class represented by {@code className} using the current thread's context class loader + * @throws NullPointerException if the className is null + * @throws ClassNotFoundException if the class is not found + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see Class#forName(String, boolean, ClassLoader) + * @see JVM: Array dimension limits in JVM Specification CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary */ - public static Class primitiveToWrapper(final Class cls) { - Class convertedClass = cls; - if (cls != null && cls.isPrimitive()) { - convertedClass = primitiveWrapperMap.get(cls); - } - return convertedClass; + public static Class getClass(final String className) throws ClassNotFoundException { + return getClass(className, true); } /** - *

      Converts the specified array of primitive Class objects to an array of - * its corresponding wrapper Class objects.

      + * Gets the class represented by {@code className} using the current thread's context class loader. This + * implementation supports the syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + *

      + * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not + * been collapsed. + *

      + *

      + * Security note: because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'} + * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating + * an untrusted class name by string comparison before calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a + * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name after + * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no + * whitespace normalization. + *

      * - * @param classes the class array to convert, may be null or empty - * @return an array which contains for each given class, the wrapper class or - * the original class if class is not a primitive. {@code null} if null input. - * Empty array if an empty array passed in. - * @since 2.1 + * @param className The class name. + * @param initialize whether the class must be initialized. + * @return The class represented by {@code className} using the current thread's context class loader. + * @throws NullPointerException if the className is null. + * @throws ClassNotFoundException if the class is not found. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see Class#forName(String, boolean, ClassLoader) + * @see JVM: Array dimension limits in JVM Specification CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary */ - public static Class[] primitivesToWrappers(final Class... classes) { - if (classes == null) { - return null; - } - - if (classes.length == 0) { - return classes; - } - - final Class[] convertedClasses = new Class[classes.length]; - for (int i = 0; i < classes.length; i++) { - convertedClasses[i] = primitiveToWrapper(classes[i]); - } - return convertedClasses; + public static Class getClass(final String className, final boolean initialize) throws ClassNotFoundException { + final ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + final ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; + return getClass(loader, className, initialize); } /** - *

      Converts the specified wrapper class to its corresponding primitive - * class.

      - * - *

      This method is the counter part of {@code primitiveToWrapper()}. - * If the passed in class is a wrapper class for a primitive type, this - * primitive type will be returned (e.g. {@code Integer.TYPE} for - * {@code Integer.class}). For other classes, or if the parameter is - * null, the return value is null.

      + * Gets the class represented by {@code className} using the {@code classLoader}, without normalizing the class name. + *

      + * Unlike {@link #getClass(ClassLoader, String, boolean)}, this method does not delete whitespace from the class name: a name that differs from + * the intended binary name only by whitespace throws {@link ClassNotFoundException}, matching {@link Class#forName(String, boolean, ClassLoader)}. Use + * this variant when the class name may come from an untrusted source, so that host-side string validation of the raw name cannot be bypassed through + * whitespace the loader would otherwise silently remove. + *

      + *

      + * The syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}" + * are still supported: a failing {@code '.'} is retried as {@code '$'} to find inner classes, so more than one dotted spelling can still resolve to the + * same inner class. When validating untrusted names, prefer validating the resolved {@link Class} object. + *

      * - * @param cls the class to convert, may be null - * @return the corresponding primitive type if {@code cls} is a - * wrapper class, null otherwise - * @see #primitiveToWrapper(Class) - * @since 2.4 - */ - public static Class wrapperToPrimitive(final Class cls) { - return wrapperPrimitiveMap.get(cls); + * @param classLoader The class loader to use to load the class. + * @param className The class name. + * @param initialize whether the class must be initialized. + * @return The class represented by {@code className} using the {@code classLoader}. + * @throws NullPointerException if the className is null. + * @throws ClassNotFoundException if the class is not found. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see Class#forName(String, boolean, ClassLoader) + * @see #getClass(ClassLoader, String, boolean) + * @since 3.21.0 + */ + public static Class getClassStrict(final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException { + return getClass(classLoader, className, initialize, false); } /** - *

      Converts the specified array of wrapper Class objects to an array of - * its corresponding primitive Class objects.

      + * Gets the (initialized) class represented by {@code className} using the current thread's context class loader, without normalizing the class name. + *

      + * Unlike {@link #getClass(String)}, this method does not delete whitespace from the class name: a name that differs from the intended binary + * name only by whitespace throws {@link ClassNotFoundException}, matching {@link Class#forName(String)}. Use this variant when the class name may come + * from an untrusted source, so that host-side string validation of the raw name cannot be bypassed through whitespace the loader would otherwise silently + * remove. + *

      + *

      + * The syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}" + * are still supported: a failing {@code '.'} is retried as {@code '$'} to find inner classes, so more than one dotted spelling can still resolve to the + * same inner class. When validating untrusted names, prefer validating the resolved {@link Class} object. + *

      * - *

      This method invokes {@code wrapperToPrimitive()} for each element - * of the passed in array.

      + * @param className The class name. + * @return The class represented by {@code className} using the current thread's context class loader. + * @throws NullPointerException if the className is null. + * @throws ClassNotFoundException if the class is not found. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see Class#forName(String) + * @see #getClass(String) + * @since 3.21.0 + */ + public static Class getClassStrict(final String className) throws ClassNotFoundException { + final ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + final ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; + return getClassStrict(loader, className, true); + } + + /** + * Delegates to {@link Class#getComponentType()} using generics. * - * @param classes the class array to convert, may be null or empty - * @return an array which contains for each given class, the primitive class or - * null if the original class is not a wrapper class. {@code null} if null input. - * Empty array if an empty array passed in. - * @see #wrapperToPrimitive(Class) - * @since 2.4 + * @param The array class type. + * @param cls A class or null. + * @return The array component type or null. + * @see Class#getComponentType() + * @since 3.13.0 */ - public static Class[] wrappersToPrimitives(final Class... classes) { - if (classes == null) { - return null; - } - - if (classes.length == 0) { - return classes; - } - - final Class[] convertedClasses = new Class[classes.length]; - for (int i = 0; i < classes.length; i++) { - convertedClasses[i] = wrapperToPrimitive(classes[i]); - } - return convertedClasses; + @SuppressWarnings("unchecked") + public static Class getComponentType(final Class cls) { + return cls == null ? null : (Class) cls.getComponentType(); } - // Inner class - // ---------------------------------------------------------------------- /** - *

      Is the specified class an inner class or static nested class.

      + * Null-safe version of {@code cls.getName()} * - * @param cls the class to check, may be null - * @return {@code true} if the class is an inner or static nested class, - * false if not or {@code null} + * @param cls The class for which to get the class name; may be null. + * @return The class name or the empty string in case the argument is {@code null}. + * @since 3.7 + * @see Class#getSimpleName() */ - public static boolean isInnerClass(final Class cls) { - return cls != null && cls.getEnclosingClass() != null; + public static String getName(final Class cls) { + return getName(cls, StringUtils.EMPTY); } - // Class loading - // ---------------------------------------------------------------------- /** - * Returns the class represented by {@code className} using the - * {@code classLoader}. This implementation supports the syntaxes - * "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", - * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + * Null-safe version of {@code cls.getName()} * - * @param classLoader the class loader to use to load the class - * @param className the class name - * @param initialize whether the class must be initialized - * @return the class represented by {@code className} using the {@code classLoader} - * @throws ClassNotFoundException if the class is not found + * @param cls The class for which to get the class name; may be null. + * @param valueIfNull The return value if the argument {@code cls} is {@code null}. + * @return The class name or {@code valueIfNull} + * @since 3.7 + * @see Class#getName() */ - public static Class getClass( - final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException { - try { - Class clazz; - if (namePrimitiveMap.containsKey(className)) { - clazz = namePrimitiveMap.get(className); - } else { - clazz = Class.forName(toCanonicalName(className), initialize, classLoader); - } - return clazz; - } catch (final ClassNotFoundException ex) { - // allow path separators (.) as inner class name separators - final int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); - - if (lastDotIndex != -1) { - try { - return getClass(classLoader, className.substring(0, lastDotIndex) + - INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), - initialize); - } catch (final ClassNotFoundException ex2) { // NOPMD - // ignore exception - } - } + public static String getName(final Class cls, final String valueIfNull) { + return getName(cls, valueIfNull, false); + } - throw ex; - } + static String getName(final Class cls, final String valueIfNull, final boolean simple) { + return cls == null ? valueIfNull : simple ? cls.getSimpleName() : cls.getName(); } /** - * Returns the (initialized) class represented by {@code className} - * using the {@code classLoader}. This implementation supports - * the syntaxes "{@code java.util.Map.Entry[]}", - * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", - * and "{@code [Ljava.util.Map$Entry;}". + * Null-safe version of {@code object.getClass().getName()} * - * @param classLoader the class loader to use to load the class - * @param className the class name - * @return the class represented by {@code className} using the {@code classLoader} - * @throws ClassNotFoundException if the class is not found + * @param object The object for which to get the class name; may be null. + * @return The class name or the empty String. + * @since 3.7 + * @see Class#getSimpleName() */ - public static Class getClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException { - return getClass(classLoader, className, true); + public static String getName(final Object object) { + return getName(object, StringUtils.EMPTY); } /** - * Returns the (initialized) class represented by {@code className} - * using the current thread's context class loader. This implementation - * supports the syntaxes "{@code java.util.Map.Entry[]}", - * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", - * and "{@code [Ljava.util.Map$Entry;}". + * Null-safe version of {@code object.getClass().getSimpleName()} * - * @param className the class name - * @return the class represented by {@code className} using the current thread's context class loader - * @throws ClassNotFoundException if the class is not found + * @param object The object for which to get the class name; may be null. + * @param valueIfNull The value to return if {@code object} is {@code null}. + * @return The class name or {@code valueIfNull}. + * @since 3.0 + * @see Class#getName() */ - public static Class getClass(final String className) throws ClassNotFoundException { - return getClass(className, true); + public static String getName(final Object object, final String valueIfNull) { + return object == null ? valueIfNull : object.getClass().getName(); } /** - * Returns the class represented by {@code className} using the - * current thread's context class loader. This implementation supports the - * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", - * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + * Gets the package name from the canonical name of a {@link Class}. * - * @param className the class name - * @param initialize whether the class must be initialized - * @return the class represented by {@code className} using the current thread's context class loader - * @throws ClassNotFoundException if the class is not found + * @param cls The class to get the package name for, may be {@code null}. + * @return The package name or an empty string. + * @since 2.4 */ - public static Class getClass(final String className, final boolean initialize) throws ClassNotFoundException { - final ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); - final ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; - return getClass(loader, className, initialize); + public static String getPackageCanonicalName(final Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageCanonicalName(cls.getName()); } - // Public method - // ---------------------------------------------------------------------- /** - *

      Returns the desired Method much like {@code Class.getMethod}, however - * it ensures that the returned Method is from a public class or interface and not - * from an anonymous inner class. This means that the Method is invokable and - * doesn't fall foul of Java bug - * 4071957).

      + * Gets the package name from the class name of an {@link Object}. * - *
      -     *  Set set = Collections.unmodifiableSet(...);
      -     *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
      -     *  Object result = method.invoke(set, new Object[]);
      -     *  
      + * @param object The class to get the package name for, may be null. + * @param valueIfNull The value to return if null. + * @return The package name of the object, or the null value. + * @since 2.4 + */ + public static String getPackageCanonicalName(final Object object, final String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageCanonicalName(object.getClass().getName()); + } + + /** + * Gets the package name from the class name. + * + *

      + * The string passed in is assumed to be a class name - it is not checked. + *

      + *

      + * If the class is in the default package, return an empty string. + *

      + * + * @param name The name to get the package name for, may be {@code null}. + * @return The package name or an empty string. + * @since 2.4 + */ + public static String getPackageCanonicalName(final String name) { + return getPackageName(getCanonicalName(name)); + } + + /** + * Gets the package name of a {@link Class}. + * + * @param cls The class to get the package name for, may be {@code null}. + * @return The package name or an empty string + */ + public static String getPackageName(final Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageName(cls.getName()); + } + + /** + * Gets the package name of an {@link Object}. * - * @param cls the class to check, not null - * @param methodName the name of the method - * @param parameterTypes the list of parameters - * @return the method - * @throws NullPointerException if the class is null - * @throws SecurityException if a security violation occurred - * @throws NoSuchMethodException if the method is not found in the given class - * or if the method doesn't conform with the requirements + * @param object The class to get the package name for, may be null. + * @param valueIfNull The value to return if null. + * @return The package name of the object, or the null value. */ - public static Method getPublicMethod(final Class cls, final String methodName, final Class... parameterTypes) - throws NoSuchMethodException { + public static String getPackageName(final Object object, final String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageName(object.getClass()); + } + /** + * Gets the package name from a {@link String}. + * + *

      + * The string passed in is assumed to be a class name. + *

      + *

      + * If the class is unpackaged, return an empty string. + *

      + * + * @param className The className to get the package name for, may be {@code null}. + * @return The package name or an empty string. + */ + public static String getPackageName(String className) { + if (StringUtils.isEmpty(className)) { + return StringUtils.EMPTY; + } + int i = 0; + // Strip array encoding + while (className.charAt(i) == '[') { + i++; + } + className = className.substring(i); + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1); + } + i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + if (i == -1) { + return StringUtils.EMPTY; + } + return className.substring(0, i); + } + + /** + * Gets the primitive class for the given class name, for example "byte". + * + * @param className The primitive class for the given class name. + * @return The primitive class. + */ + static Class getPrimitiveClass(final String className) { + return NAME_PRIMITIVE_MAP.get(className); + } + + /** + * Gets the desired Method much like {@code Class.getMethod}, however it ensures that the returned Method is from a + * public class or interface and not from an anonymous inner class. This means that the Method is invokable and doesn't + * fall foul of Java bug (4071957). + * + *
      +     *  {@code Set set = Collections.unmodifiableSet(...);
      +     *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
      +     *  Object result = method.invoke(set, new Object[]);}
      +     * 
      + * + * @param cls The class to check, not null. + * @param methodName The name of the method. + * @param parameterTypes The list of parameters. + * @return The method. + * @throws NullPointerException if the class is null. + * @throws SecurityException if a security violation occurred. + * @throws NoSuchMethodException if the method is not found in the given class or if the method doesn't conform with the + * requirements. + */ + public static Method getPublicMethod(final Class cls, final String methodName, final Class... parameterTypes) throws NoSuchMethodException { final Method declaredMethod = cls.getMethod(methodName, parameterTypes); - if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) { + if (isPublic(declaredMethod.getDeclaringClass())) { return declaredMethod; } - - final List> candidateClasses = new ArrayList<>(); - candidateClasses.addAll(getAllInterfaces(cls)); + final List> candidateClasses = new ArrayList<>(getAllInterfaces(cls)); candidateClasses.addAll(getAllSuperclasses(cls)); - for (final Class candidateClass : candidateClasses) { - if (!Modifier.isPublic(candidateClass.getModifiers())) { + if (!isPublic(candidateClass)) { continue; } - Method candidateMethod; - try { - candidateMethod = candidateClass.getMethod(methodName, parameterTypes); - } catch (final NoSuchMethodException ex) { - continue; + final Method candidateMethod; + try { + candidateMethod = candidateClass.getMethod(methodName, parameterTypes); + } catch (final NoSuchMethodException ex) { + continue; + } + if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { + return candidateMethod; + } + } + throw new NoSuchMethodException("Can't find a public method for " + methodName + " " + ArrayUtils.toString(parameterTypes)); + } + + /** + * Gets the canonical name minus the package name from a {@link Class}. + * + * @param cls The class for which to get the short canonical class name; may be null. + * @return The canonical name without the package name or an empty string. + * @since 2.4 + * @see Class#getCanonicalName() + */ + public static String getShortCanonicalName(final Class cls) { + return cls == null ? StringUtils.EMPTY : getShortCanonicalName(cls.getCanonicalName()); + } + + /** + * Gets the canonical name minus the package name for an {@link Object}. + * + * @param object The class to get the short name for, may be null. + * @param valueIfNull The value to return if null. + * @return The canonical name of the object without the package name, or the null value. + * @since 2.4 + * @see Class#getCanonicalName() + */ + public static String getShortCanonicalName(final Object object, final String valueIfNull) { + return object == null ? valueIfNull : getShortCanonicalName(object.getClass()); + } + + /** + * Gets the canonical name minus the package name from a String. + * + *

      + * The string passed in is assumed to be a class name - it is not checked. + *

      + * + *

      + * Note that this method is mainly designed to handle the arrays and primitives properly. If the class is an inner class + * then the result value will not contain the outer classes. This way the behavior of this method is different from + * {@link #getShortClassName(String)}. The argument in that case is class name and not canonical name and the return + * value retains the outer classes. + *

      + * + *

      + * Note that there is no way to reliably identify the part of the string representing the package hierarchy and the part + * that is the outer class or classes in case of an inner class. Trying to find the class would require reflective call + * and the class itself may not even be on the class path. Relying on the fact that class names start with capital + * letter and packages with lower case is heuristic. + *

      + * + *

      + * It is recommended to use {@link #getShortClassName(String)} for cases when the class is an inner class and use this + * method for cases it is designed for. + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Examples
      return valueinput
      {@code ""}{@code (String) null}
      {@code "Map.Entry"}{@code java.util.Map.Entry.class.getName()}
      {@code "Entry"}{@code java.util.Map.Entry.class.getCanonicalName()}
      {@code "ClassUtils"}{@code "org.apache.commons.lang3.ClassUtils"}
      {@code "ClassUtils[]"}{@code "[Lorg.apache.commons.lang3.ClassUtils;"}
      {@code "ClassUtils[][]"}{@code "[[Lorg.apache.commons.lang3.ClassUtils;"}
      {@code "ClassUtils[]"}{@code "org.apache.commons.lang3.ClassUtils[]"}
      {@code "ClassUtils[][]"}{@code "org.apache.commons.lang3.ClassUtils[][]"}
      {@code "int[]"}{@code "[I"}
      {@code "int[]"}{@code int[].class.getCanonicalName()}
      {@code "int[]"}{@code int[].class.getName()}
      {@code "int[][]"}{@code "[[I"}
      {@code "int[]"}{@code "int[]"}
      {@code "int[][]"}{@code "int[][]"}
      + * + * @param canonicalName The class name to get the short name for. + * @return The canonical name of the class without the package name or an empty string. + * @since 2.4 + */ + public static String getShortCanonicalName(final String canonicalName) { + return getShortClassName(getCanonicalName(canonicalName)); + } + + /** + * Gets the class name minus the package name from a {@link Class}. + * + * @param cls The class to get the short name for. + * @return The class name without the package name or an empty string. If the class is an inner class then the returned + * value will contain the outer class or classes separated with {@code .} (dot) character. + */ + public static String getShortClassName(final Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + int dim = 0; + Class c = cls; + while (c.isArray()) { + dim++; + c = c.getComponentType(); + } + String base; + // c.isAnonymousClass() / isLocalClass() and the getDeclaringClass() chain + // can both throw NoClassDefFoundError when the enclosing class is + // missing from the classpath, so the try/catch wraps the whole block. + try { + // Preserve legacy behavior for anonymous/local classes (keeps compiler ordinals: $13, $10Named, etc.) + if (c.isAnonymousClass() || c.isLocalClass()) { + base = getShortClassName(c.getName()); + } else { + final Deque parts = new ArrayDeque<>(); + Class x = c; + while (x != null) { + parts.push(x.getSimpleName()); + x = x.getDeclaringClass(); + } + base = String.join(".", parts); + } + } catch (final NoClassDefFoundError ignored) { + base = getShortClassName(c.getName()); + } + return base + StringUtils.repeat("[]", dim); + } + + /** + * Gets the class name of the {@code object} without the package name or names. + * + * @param object The class to get the short name for, may be {@code null}. + * @param valueIfNull The value to return if the object is {@code null}. + * @return The class name of the object without the package name, or {@code valueIfNull} if the argument {@code object} + * is {@code null}. + */ + public static String getShortClassName(final Object object, final String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getShortClassName(object.getClass()); + } + + /** + * Gets the class name minus the package name from a String. + * + *

      + * The string passed in is assumed to be a class name - it is not checked. The string has to be formatted the way as the + * JDK method {@code Class.getName()} returns it, and not the usual way as we write it, for example in import + * statements, or as it is formatted by {@code Class.getCanonicalName()}. + *

      + * + *

      + * The difference is significant only in case of classes that are inner classes of some other classes. In this case + * the separator between the outer and inner class (possibly on multiple hierarchy level) has to be {@code $} (dollar + * sign) and not {@code .} (dot), as it is returned by {@code Class.getName()} + *

      + * + *

      + * Note that this method is called from the {@link #getShortClassName(Class)} method using the string returned by + * {@code Class.getName()}. + *

      + * + *

      + * Note that this method differs from {@link #getSimpleName(Class)} in that this will return, for example + * {@code "Map.Entry"} whilst the {@link Class} variant will simply return {@code "Entry"}. In this example + * the argument {@code className} is the string {@code java.util.Map$Entry} (note the {@code $} sign). + *

      + * + * @param className The className to get the short name for. It has to be formatted as returned by + * {@code Class.getName()} and not {@code Class.getCanonicalName()}. + * @return The class name of the class without the package name or an empty string. If the class is an inner class then + * value contains the outer class or classes and the separator is replaced to be {@code .} (dot) character. + */ + public static String getShortClassName(String className) { + if (StringUtils.isEmpty(className)) { + return StringUtils.EMPTY; + } + final StringBuilder arrayPrefix = new StringBuilder(); + // Handle array encoding + if (className.startsWith("[")) { + while (className.charAt(0) == '[') { + className = className.substring(1); + arrayPrefix.append("[]"); + } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1, className.length() - 1); + } + if (REVERSE_ABBREVIATION_MAP.containsKey(className)) { + className = REVERSE_ABBREVIATION_MAP.get(className); + } + } + final int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + final int innerIdx = className.indexOf(INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); + String out = className.substring(lastDotIdx + 1); + if (innerIdx != -1) { + out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); + } + return out + arrayPrefix; + } + + /** + * Null-safe version of {@code cls.getSimpleName()} + * + * @param cls The class for which to get the simple name; may be null. + * @return The simple class name or the empty string in case the argument is {@code null}. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(final Class cls) { + return getSimpleName(cls, StringUtils.EMPTY); + } + + /** + * Null-safe version of {@code cls.getSimpleName()} + * + * @param cls The class for which to get the simple name; may be null. + * @param valueIfNull The value to return if null. + * @return The simple class name or {@code valueIfNull} if the argument {@code cls} is {@code null}. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(final Class cls, final String valueIfNull) { + return cls == null ? valueIfNull : cls.getSimpleName(); + } + + /** + * Null-safe version of {@code object.getClass().getSimpleName()} + * + *

      + * It is to note that this method is overloaded and in case the argument {@code object} is a {@link Class} object then + * the {@link #getSimpleName(Class)} will be invoked. If this is a significant possibility then the caller should check + * this case and call {@code + * getSimpleName(Class.class)} or just simply use the string literal {@code "Class"}, which is the result of the method + * in that case. + *

      + * + * @param object The object for which to get the simple class name; may be null. + * @return The simple class name or the empty string in case the argument is {@code null}. + * @since 3.7 + * @see Class#getSimpleName() + */ + public static String getSimpleName(final Object object) { + return getSimpleName(object, StringUtils.EMPTY); + } + + /** + * Null-safe version of {@code object.getClass().getSimpleName()} + * + * @param object The object for which to get the simple class name; may be null. + * @param valueIfNull The value to return if {@code object} is {@code null}. + * @return The simple class name or {@code valueIfNull} if the argument {@code object} is {@code null}. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(final Object object, final String valueIfNull) { + return object == null ? valueIfNull : object.getClass().getSimpleName(); + } + + /** + * Gets an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order, + * excluding interfaces. + * + * @param type The type to get the class hierarchy from. + * @return Iterable an Iterable over the class hierarchy of the given class. + * @since 3.2 + */ + public static Iterable> hierarchy(final Class type) { + return hierarchy(type, Interfaces.EXCLUDE); + } + + /** + * Gets an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order. + * + * @param type The type to get the class hierarchy from. + * @param interfacesBehavior switch indicating whether to include or exclude interfaces. + * @return Iterable an Iterable over the class hierarchy of the given class. + * @since 3.2 + */ + public static Iterable> hierarchy(final Class type, final Interfaces interfacesBehavior) { + final Iterable> classes = () -> { + final AtomicReference> next = new AtomicReference<>(type); + return new Iterator>() { + + @Override + public boolean hasNext() { + return next.get() != null; + } + + @Override + public Class next() { + return next.getAndUpdate(Class::getSuperclass); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + }; + if (interfacesBehavior != Interfaces.INCLUDE) { + return classes; + } + return () -> { + final Set> seenInterfaces = new HashSet<>(); + final Iterator> wrapped = classes.iterator(); + + return new Iterator>() { + Iterator> interfaces = Collections.emptyIterator(); + + @Override + public boolean hasNext() { + return interfaces.hasNext() || wrapped.hasNext(); + } + + @Override + public Class next() { + if (interfaces.hasNext()) { + final Class nextInterface = interfaces.next(); + seenInterfaces.add(nextInterface); + return nextInterface; + } + final Class nextSuperclass = wrapped.next(); + final Set> currentInterfaces = new LinkedHashSet<>(); + walkInterfaces(currentInterfaces, nextSuperclass); + interfaces = currentInterfaces.iterator(); + return nextSuperclass; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private void walkInterfaces(final Set> addTo, final Class c) { + for (final Class iface : c.getInterfaces()) { + if (!seenInterfaces.contains(iface)) { + addTo.add(iface); + } + walkInterfaces(addTo, iface); + } + } + + }; + }; + } + + /** + * Tests whether one {@link Class} can be assigned to a variable of another {@link Class}. + * + *

      + * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of + * primitive classes and {@code null}s. + *

      + * + *

      + * Primitive widenings allow an int to be assigned to a long, float or double. This method returns the correct result + * for these cases. + *

      + * + *

      + * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in + * and the toClass is non-primitive. + *

      + * + *

      + * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be + * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or + * widening reference conversion. See The Java Language + * Specification, sections 5.1.1, 5.1.2 and 5.1.4 for details. + *

      + * + *

      + * Since Lang 3.0, this method will default behavior for calculating assignability between primitive + * and wrapper types corresponding to the running Java version; i.e. autoboxing will be the default behavior in + * VMs running Java versions > 1.5. + *

      + * + * @param cls The Class to check, may be null. + * @param toClass The Class to try to assign into, returns false if null. + * @return {@code true} if assignment possible. + */ + public static boolean isAssignable(final Class cls, final Class toClass) { + return isAssignable(cls, toClass, true); + } + + /** + * Tests whether one {@link Class} can be assigned to a variable of another {@link Class}. + * + *

      + * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of + * primitive classes and {@code null}s. + *

      + * + *

      + * Primitive widenings allow an int to be assigned to a long, float or double. This method returns the correct result + * for these cases. + *

      + * + *

      + * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in + * and the toClass is non-primitive. + *

      + * + *

      + * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be + * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or + * widening reference conversion. See The Java Language + * Specification, sections 5.1.1, 5.1.2 and 5.1.4 for details. + *

      + * + * @param cls The Class to check, may be null. + * @param toClass The Class to try to assign into, returns false if null. + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers. + * @return {@code true} if assignment possible. + */ + public static boolean isAssignable(Class cls, final Class toClass, final boolean autoboxing) { + if (toClass == null) { + return false; + } + // have to check for null, as isAssignableFrom doesn't + if (cls == null) { + return !toClass.isPrimitive(); + } + // autoboxing: + if (autoboxing) { + if (cls.isPrimitive() && !toClass.isPrimitive()) { + cls = primitiveToWrapper(cls); + if (cls == null) { + return false; + } + } + if (toClass.isPrimitive() && !cls.isPrimitive()) { + cls = wrapperToPrimitive(cls); + if (cls == null) { + return false; + } + } + } + if (cls.equals(toClass)) { + return true; + } + if (cls.isPrimitive()) { + if (!toClass.isPrimitive()) { + return false; + } + if (Integer.TYPE.equals(cls)) { + return Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); + } + if (Long.TYPE.equals(cls)) { + return Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); + } + if (Boolean.TYPE.equals(cls)) { + return false; + } + if (Double.TYPE.equals(cls)) { + return false; + } + if (Float.TYPE.equals(cls)) { + return Double.TYPE.equals(toClass); + } + if (Character.TYPE.equals(cls) || Short.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } - if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { - return candidateMethod; + if (Byte.TYPE.equals(cls)) { + return Short.TYPE.equals(toClass) || Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); } + // should never get here + return false; } - - throw new NoSuchMethodException("Can't find a public method for " + - methodName + " " + ArrayUtils.toString(parameterTypes)); + return toClass.isAssignableFrom(cls); } - // ---------------------------------------------------------------------- /** - * Converts a class name to a JLS style class name. + * Tests whether an array of Classes can be assigned to another array of Classes. + * + *

      + * This method calls {@link #isAssignable(Class, Class) isAssignable} for each Class pair in the input arrays. It can be + * used to check if a set of arguments (the first parameter) are suitably compatible with a set of method parameter + * types (the second parameter). + *

      + * + *

      + * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of + * primitive classes and {@code null}s. + *

      + * + *

      + * Primitive widenings allow an int to be assigned to a {@code long}, {@code float} or {@code double}. This method + * returns the correct result for these cases. + *

      + * + *

      + * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in + * and the toClass is non-primitive. + *

      + * + *

      + * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be + * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or + * widening reference conversion. See The Java Language + * Specification, sections 5.1.1, 5.1.2 and 5.1.4 for details. + *

      + * + *

      + * Since Lang 3.0, this method will default behavior for calculating assignability between primitive + * and wrapper types corresponding to the running Java version; i.e. autoboxing will be the default behavior in + * VMs running Java versions > 1.5. + *

      * - * @param className the class name - * @return the converted name + * @param classArray The array of Classes to check, may be {@code null}. + * @param toClassArray The array of Classes to try to assign into, may be {@code null}. + * @return {@code true} if assignment possible. */ - private static String toCanonicalName(String className) { - className = StringUtils.deleteWhitespace(className); - Validate.notNull(className, "className must not be null."); - if (className.endsWith("[]")) { - final StringBuilder classNameBuffer = new StringBuilder(); - while (className.endsWith("[]")) { - className = className.substring(0, className.length() - 2); - classNameBuffer.append("["); - } - final String abbreviation = abbreviationMap.get(className); - if (abbreviation != null) { - classNameBuffer.append(abbreviation); - } else { - classNameBuffer.append("L").append(className).append(";"); - } - className = classNameBuffer.toString(); - } - return className; + public static boolean isAssignable(final Class[] classArray, final Class... toClassArray) { + return isAssignable(classArray, toClassArray, true); } /** - *

      Converts an array of {@code Object} in to an array of {@code Class} objects. - * If any of these objects is null, a null element will be inserted into the array.

      + * Tests whether an array of Classes can be assigned to another array of Classes. * - *

      This method returns {@code null} for a {@code null} input array.

      + *

      + * This method calls {@link #isAssignable(Class, Class) isAssignable} for each Class pair in the input arrays. It can be + * used to check if a set of arguments (the first parameter) are suitably compatible with a set of method parameter + * types (the second parameter). + *

      * - * @param array an {@code Object} array - * @return a {@code Class} array, {@code null} if null array input - * @since 2.4 + *

      + * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of + * primitive classes and {@code null}s. + *

      + * + *

      + * Primitive widenings allow an int to be assigned to a {@code long}, {@code float} or {@code double}. This method + * returns the correct result for these cases. + *

      + * + *

      + * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in + * and the toClass is non-primitive. + *

      + * + *

      + * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be + * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or + * widening reference conversion. See The Java Language + * Specification, sections 5.1.1, 5.1.2 and 5.1.4 for details. + *

      + * + * @param classArray The array of Classes to check, may be {@code null} + * @param toClassArray The array of Classes to try to assign into, may be {@code null} + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return {@code true} if assignment possible */ - public static Class[] toClass(final Object... array) { - if (array == null) { - return null; - } else if (array.length == 0) { - return ArrayUtils.EMPTY_CLASS_ARRAY; + public static boolean isAssignable(Class[] classArray, Class[] toClassArray, final boolean autoboxing) { + if (!ArrayUtils.isSameLength(classArray, toClassArray)) { + return false; } - final Class[] classes = new Class[array.length]; - for (int i = 0; i < array.length; i++) { - classes[i] = array[i] == null ? null : array[i].getClass(); + classArray = ArrayUtils.nullToEmpty(classArray); + toClassArray = ArrayUtils.nullToEmpty(toClassArray); + for (int i = 0; i < classArray.length; i++) { + if (!isAssignable(classArray[i], toClassArray[i], autoboxing)) { + return false; + } } - return classes; + return true; } - // Short canonical name - // ---------------------------------------------------------------------- /** - *

      Gets the canonical name minus the package name for an {@code Object}.

      + * Tests whether the specified class an inner class or static nested class. * - * @param object the class to get the short name for, may be null - * @param valueIfNull the value to return if null - * @return the canonical name of the object without the package name, or the null value - * @since 2.4 + * @param cls The class to check, may be null. + * @return {@code true} if the class is an inner or static nested class, false if not or {@code null}. */ - public static String getShortCanonicalName(final Object object, final String valueIfNull) { - if (object == null) { - return valueIfNull; - } - return getShortCanonicalName(object.getClass().getName()); + public static boolean isInnerClass(final Class cls) { + return cls != null && cls.getEnclosingClass() != null; } /** - *

      Gets the canonical class name for a {@code Class}.

      + * Tests whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, + * {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). * - * @param cls the class for which to get the canonical class name; may be null - * @return the canonical name of the class, or the empty String - * @since 3.7 - * @see Class#getCanonicalName() + * @param type The class to query or null. + * @return true if the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, + * {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * @since 3.1 */ - public static String getCanonicalName(final Class cls) { - return getCanonicalName(cls, StringUtils.EMPTY); + public static boolean isPrimitiveOrWrapper(final Class type) { + return type != null && type.isPrimitive() || isPrimitiveWrapper(type); } /** - *

      Gets the canonical name for a {@code Class}.

      + * Tests whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, + * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). * - * @param cls the class for which to get the canonical class name; may be null - * @param valueIfNull the return value if null - * @return the canonical name of the class, or {@code valueIfNull} - * @since 3.7 - * @see Class#getCanonicalName() + * @param type The class to query or null. + * @return true if the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, + * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * @since 3.1 */ - public static String getCanonicalName(final Class cls, final String valueIfNull) { - if (cls == null) { - return valueIfNull; - } - final String canonicalName = cls.getCanonicalName(); - return canonicalName == null ? valueIfNull : canonicalName; + public static boolean isPrimitiveWrapper(final Class type) { + return WRAPPER_PRIMITIVE_MAP.containsKey(type); } /** - *

      Gets the canonical name for an {@code Object}.

      + * Tests whether a {@link Class} is public. * - * @param object the object for which to get the canonical class name; may be null - * @return the canonical name of the object, or the empty String - * @since 3.7 - * @see Class#getCanonicalName() + * @param cls Class to test. + * @return {@code true} if {@code cls} is public. + * @since 3.13.0 */ - public static String getCanonicalName(final Object object) { - return getCanonicalName(object, StringUtils.EMPTY); + public static boolean isPublic(final Class cls) { + return Modifier.isPublic(cls.getModifiers()); } /** - *

      Gets the canonical name for an {@code Object}.

      + * Converts the specified array of primitive Class objects to an array of its corresponding wrapper Class objects. * - * @param object the object for which to get the canonical class name; may be null - * @param valueIfNull the return value if null - * @return the canonical name of the object or {@code valueIfNull} - * @since 3.7 - * @see Class#getCanonicalName() + * @param classes The class array to convert, may be null or empty. + * @return An array which contains for each given class, the wrapper class or the original class if class is not a primitive. {@code null} if null input. + * Empty array if an empty array passed in. + * @since 2.1 */ - public static String getCanonicalName(final Object object, final String valueIfNull) { - if (object == null) { - return valueIfNull; + public static Class[] primitivesToWrappers(final Class... classes) { + if (classes == null) { + return null; } - final String canonicalName = object.getClass().getCanonicalName(); - return canonicalName == null ? valueIfNull : canonicalName; + if (classes.length == 0) { + return classes; + } + return ArrayUtils.setAll(new Class[classes.length], i -> primitiveToWrapper(classes[i])); } /** - *

      Gets the canonical name minus the package name from a {@code Class}.

      + * Converts the specified primitive Class object to its corresponding wrapper Class object. * - * @param cls the class for which to get the short canonical class name; may be null - * @return the canonical name without the package name or an empty string - * @since 2.4 + *

      + * NOTE: From v2.2, this method handles {@code Void.TYPE}, returning {@code Void.TYPE}. + *

      + * + * @param cls The class to convert, may be null. + * @return The wrapper class for {@code cls} or {@code cls} if {@code cls} is not a primitive. {@code null} if null input. + * @since 2.1 */ - public static String getShortCanonicalName(final Class cls) { - if (cls == null) { - return StringUtils.EMPTY; - } - return getShortCanonicalName(cls.getName()); + public static Class primitiveToWrapper(final Class cls) { + return cls != null && cls.isPrimitive() ? PRIMITIVE_WRAPPER_MAP.get(cls) : cls; } /** - *

      Gets the canonical name minus the package name from a String.

      - * - *

      The string passed in is assumed to be a class name - it is not checked.

      - * - *

      Note that this method is mainly designed to handle the arrays and primitives properly. - * If the class is an inner class then the result value will not contain the outer classes. - * This way the behavior of this method is different from {@link #getShortClassName(String)}. - * The argument in that case is class name and not canonical name and the return value - * retains the outer classes.

      - * - *

      Note that there is no way to reliably identify the part of the string representing the - * package hierarchy and the part that is the outer class or classes in case of an inner class. - * Trying to find the class would require reflective call and the class itself may not even be - * on the class path. Relying on the fact that class names start with capital letter and packages - * with lower case is heuristic.

      + * Converts an array of {@link Object} in to an array of {@link Class} objects. If any of these objects is null, a null element will be inserted into the + * array. * - *

      It is recommended to use {@link #getShortClassName(String)} for cases when the class - * is an inner class and use this method for cases it is designed for.

      - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
      Examples
      return valueinput
      {@code ""}{@code (String)null}
      {@code "Map.Entry"}{@code java.util.Map.Entry.class.getName()}
      {@code "Entry"}{@code java.util.Map.Entry.class.getCanonicalName()}
      {@code "ClassUtils"}{@code "org.apache.commons.lang3.ClassUtils"}
      {@code "ClassUtils[]"}{@code "[Lorg.apache.commons.lang3.ClassUtils;"}
      {@code "ClassUtils[][]"}{@code "[[Lorg.apache.commons.lang3.ClassUtils;"}
      {@code "ClassUtils[]"}{@code "org.apache.commons.lang3.ClassUtils[]"}
      {@code "ClassUtils[][]"}{@code "org.apache.commons.lang3.ClassUtils[][]"}
      {@code "int[]"}{@code "[I"}
      {@code "int[]"}{@code int[].class.getCanonicalName()}
      {@code "int[]"}{@code int[].class.getName()}
      {@code "int[][]"}{@code "[[I"}
      {@code "int[]"}{@code "int[]"}
      {@code "int[][]"}{@code "int[][]"}
      + *

      + * This method returns {@code null} for a {@code null} input array. + *

      * - * @param canonicalName the class name to get the short name for - * @return the canonical name of the class without the package name or an empty string + * @param array An {@link Object} array. + * @return A {@link Class} array, {@code null} if null array input. * @since 2.4 */ - public static String getShortCanonicalName(final String canonicalName) { - return getShortClassName(getCanonicalName(canonicalName)); + public static Class[] toClass(final Object... array) { + if (array == null) { + return null; + } + if (array.length == 0) { + return ArrayUtils.EMPTY_CLASS_ARRAY; + } + return ArrayUtils.setAll(new Class[array.length], i -> array[i] == null ? null : array[i].getClass()); } - // Package name - // ---------------------------------------------------------------------- /** - *

      Gets the package name from the class name of an {@code Object}.

      + * Converts and cleans up a class name to a JLS style class name. + *

      + * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not + * been collapsed. + *

      * - * @param object the class to get the package name for, may be null - * @param valueIfNull the value to return if null - * @return the package name of the object, or the null value - * @since 2.4 - */ - public static String getPackageCanonicalName(final Object object, final String valueIfNull) { - if (object == null) { - return valueIfNull; - } - return getPackageCanonicalName(object.getClass().getName()); + * @param className The class name. + * @return The converted name. + * @throws NullPointerException if the className is null. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. + * @see JVM: Array dimension limits in JVM Specification + * CONSTANT_Class_info + * @see JLS: Fully Qualified Names and Canonical Names + * @see JLS: The Form of a Binary + */ + private static String toCleanName(final String className) { + return toEncodedName(StringUtils.deleteWhitespace(className)); } /** - *

      Gets the package name from the canonical name of a {@code Class}.

      + * Converts a class name to a JLS style class name without normalizing whitespace. * - * @param cls the class to get the package name for, may be {@code null}. - * @return the package name or an empty string - * @since 2.4 + * @param className The class name. + * @return The converted name. + * @throws NullPointerException if the className is null. + * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255. + * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535. */ - public static String getPackageCanonicalName(final Class cls) { - if (cls == null) { - return StringUtils.EMPTY; + private static String toEncodedName(final String className) { + String canonicalName = className; + Objects.requireNonNull(canonicalName, "className"); + if (canonicalName.isEmpty()) { + throw new IllegalArgumentException("Class name is empty"); } - return getPackageCanonicalName(cls.getName()); + final String encodedArrayOpen = "["; + final String encodedClassNameStart = "L"; + final String encodedClassNameEnd = ";"; + final boolean encodedName = canonicalName.startsWith(encodedArrayOpen) && canonicalName.endsWith(encodedClassNameEnd); + if (encodedName) { + final int arrIdx = canonicalName.indexOf(encodedClassNameStart); + if (arrIdx > MAX_JVM_ARRAY_DIMENSION) { + throw new IllegalArgumentException("Array dimension greater than JVM specification maximum of 255."); + } + if (arrIdx < 0) { + throw new IllegalArgumentException("Expected 'L' after '[' for an array style string."); + } + final int cnLen = canonicalName.length() - (arrIdx + 2); // account for the ending ';' + if (cnLen > MAX_CLASS_NAME_LENGTH) { + throw new IllegalArgumentException(String.format("Class name greater than maxium length %,d", MAX_CLASS_NAME_LENGTH)); + } + } + final String arrayMarker = "[]"; + final int arrIdx = canonicalName.indexOf(arrayMarker); + // The class name length without array markers. + final int cnLen = arrIdx > 0 ? arrIdx : canonicalName.length(); + if (cnLen > MAX_CLASS_NAME_LENGTH && !encodedName) { + throw new IllegalArgumentException(String.format("Class name greater than maxium length %,d", MAX_CLASS_NAME_LENGTH)); + } + if (canonicalName.endsWith(arrayMarker)) { + // Reject malformed inputs like "java.lang.String[]junk[]" or + // "java.lang.String[]][]" where the suffix is not composed of + // repeated "[]" pairs. + final String tail = canonicalName.substring(arrIdx); + if (!ARRAY_TAIL_PATTERN.matcher(tail).matches()) { + throw new IllegalArgumentException("Malformed array name: " + canonicalName); + } + final int dims = (canonicalName.length() - arrIdx) / 2; + if (dims > MAX_JVM_ARRAY_DIMENSION) { + throw new IllegalArgumentException("Array dimension greater than JVM specification maximum of 255."); + } + final StringBuilder classNameBuffer = new StringBuilder(StringUtils.repeat(encodedArrayOpen, dims)); + canonicalName = canonicalName.substring(0, arrIdx); + final String abbreviation = ABBREVIATION_MAP.get(canonicalName); + if (abbreviation != null) { + classNameBuffer.append(abbreviation); + } else { + classNameBuffer.append(encodedClassNameStart).append(canonicalName).append(encodedClassNameEnd); + } + canonicalName = classNameBuffer.toString(); + } + return canonicalName; } /** - *

      Gets the package name from the class name.

      + * Decides if the part that was just copied to its destination location in the work array can be kept as it was copied + * or must be abbreviated. It must be kept when the part is the last one, which is the simple name of the class. In this + * case the {@code source} index, from where the characters are copied points one position after the last character, + * a.k.a. {@code source == + * originalLength} * - *

      The string passed in is assumed to be a class name - it is not checked.

      - *

      If the class is in the default package, return an empty string.

      + *

      + * If the part is not the last one then it can be kept unabridged if the number of the characters copied so far plus the + * character that are to be copied is less than or equal to the desired length. + *

      * - * @param name the name to get the package name for, may be {@code null} - * @return the package name or an empty string - * @since 2.4 + * @param runAheadTarget The target index (where the characters were copied to) pointing after the last character copied + * when the current part was copied. + * @param source The source index (where the characters were copied from) pointing after the last character copied when + * the current part was copied. + * @param originalLength The original length of the class full name, which is abbreviated. + * @param desiredLength The desired length of the abbreviated class name. + * @return {@code true} if it can be kept in its original length; {@code false} if the current part has to be abbreviated. */ - public static String getPackageCanonicalName(final String name) { - return getPackageName(getCanonicalName(name)); + private static boolean useFull(final int runAheadTarget, final int source, final int originalLength, final int desiredLength) { + return source >= originalLength || runAheadTarget + originalLength - source <= desiredLength; } /** - *

      Converts a given name of class into canonical format. - * If name of class is not a name of array class it returns - * unchanged name.

      + * Converts the specified array of wrapper Class objects to an array of its corresponding primitive Class objects. * - *

      The method does not change the {@code $} separators in case - * the class is inner class.

      - * - *

      Example: - *

        - *
      • {@code getCanonicalName("[I") = "int[]"}
      • - *
      • {@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}
      • - *
      • {@code getCanonicalName("java.lang.String") = "java.lang.String"}
      • - *
      + *

      + * This method invokes {@code wrapperToPrimitive()} for each element of the passed in array. *

      * - * @param className the name of class - * @return canonical form of class name + * @param classes The class array to convert, may be null or empty. + * @return An array which contains for each given class, the primitive class or {@code null} if the original class is not a wrapper class. + * {@code null} if null input. Empty array if an empty array passed in. + * @see #wrapperToPrimitive(Class) * @since 2.4 */ - private static String getCanonicalName(String className) { - className = StringUtils.deleteWhitespace(className); - if (className == null) { + public static Class[] wrappersToPrimitives(final Class... classes) { + if (classes == null) { return null; } - int dim = 0; - while (className.startsWith("[")) { - dim++; - className = className.substring(1); - } - if (dim < 1) { - return className; - } - if (className.startsWith("L")) { - className = className.substring( - 1, - className.endsWith(";") - ? className.length() - 1 - : className.length()); - } else { - if (!className.isEmpty()) { - className = reverseAbbreviationMap.get(className.substring(0, 1)); - } - } - final StringBuilder canonicalClassNameBuffer = new StringBuilder(className); - for (int i = 0; i < dim; i++) { - canonicalClassNameBuffer.append("[]"); + if (classes.length == 0) { + return classes; } - return canonicalClassNameBuffer.toString(); + return ArrayUtils.setAll(new Class[classes.length], i -> wrapperToPrimitive(classes[i])); } /** - * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order, - * excluding interfaces. + * Converts the specified wrapper class to its corresponding primitive class. * - * @param type the type to get the class hierarchy from - * @return Iterable an Iterable over the class hierarchy of the given class - * @since 3.2 + *

      + * This method is the counter part of {@code primitiveToWrapper()}. If the passed in class is a wrapper class for a + * primitive type, this primitive type will be returned (e.g. {@code Integer.TYPE} for {@code Integer.class}). For other + * classes, or if the parameter is {@code null}, the return value is {@code null}. + *

      + * + * @param cls The class to convert, may be {@code null}. + * @return The corresponding primitive type if {@code cls} is a wrapper class, {@code null} otherwise. + * @see #primitiveToWrapper(Class) + * @since 2.4 */ - public static Iterable> hierarchy(final Class type) { - return hierarchy(type, Interfaces.EXCLUDE); + public static Class wrapperToPrimitive(final Class cls) { + return WRAPPER_PRIMITIVE_MAP.get(cls); } /** - * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order. + * ClassUtils instances should NOT be constructed in standard programming. Instead, the class should be used as + * {@code ClassUtils.getShortClassName(cls)}. * - * @param type the type to get the class hierarchy from - * @param interfacesBehavior switch indicating whether to include or exclude interfaces - * @return Iterable an Iterable over the class hierarchy of the given class - * @since 3.2 + *

      + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

      + * + * @deprecated TODO Make private in 4.0. */ - public static Iterable> hierarchy(final Class type, final Interfaces interfacesBehavior) { - final Iterable> classes = () -> { - final MutableObject> next = new MutableObject<>(type); - return new Iterator>() { - - @Override - public boolean hasNext() { - return next.getValue() != null; - } - - @Override - public Class next() { - final Class result = next.getValue(); - next.setValue(result.getSuperclass()); - return result; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - }; - if (interfacesBehavior != Interfaces.INCLUDE) { - return classes; - } - return () -> { - final Set> seenInterfaces = new HashSet<>(); - final Iterator> wrapped = classes.iterator(); - - return new Iterator>() { - Iterator> interfaces = Collections.>emptySet().iterator(); - - @Override - public boolean hasNext() { - return interfaces.hasNext() || wrapped.hasNext(); - } - - @Override - public Class next() { - if (interfaces.hasNext()) { - final Class nextInterface = interfaces.next(); - seenInterfaces.add(nextInterface); - return nextInterface; - } - final Class nextSuperclass = wrapped.next(); - final Set> currentInterfaces = new LinkedHashSet<>(); - walkInterfaces(currentInterfaces, nextSuperclass); - interfaces = currentInterfaces.iterator(); - return nextSuperclass; - } - - private void walkInterfaces(final Set> addTo, final Class c) { - for (final Class iface : c.getInterfaces()) { - if (!seenInterfaces.contains(iface)) { - addTo.add(iface); - } - walkInterfaces(addTo, iface); - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - }; + @Deprecated + public ClassUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/Conversion.java b/src/main/java/org/apache/commons/lang3/Conversion.java index 18873d917b9..cf43e36c371 100644 --- a/src/main/java/org/apache/commons/lang3/Conversion.java +++ b/src/main/java/org/apache/commons/lang3/Conversion.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,19 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.commons.lang3; import java.util.UUID; - /** - *

      * Static methods to convert a type into another, with endianness and bit ordering awareness. - *

      + * *

      - * The methods names follow a naming rule:
      - * {@code [source endianness][source bit ordering]To[destination endianness][destination bit ordering]} + * The methods names follow a naming rule: *

      + *
      {@code
      + * [source endianness][source bit ordering]To[destination endianness][destination bit ordering]
      + * }
      *

      * Source/destination type fields is one of the following: *

      @@ -36,275 +37,170 @@ *
    • int or intArray
    • *
    • long or longArray
    • *
    • hex: a String containing hexadecimal digits (lowercase in destination)
    • - *
    • hexDigit: a Char containing a hexadecimal digit (lowercase in destination)
    • + *
    • hexDigit: a {@code char} containing a hexadecimal digit (lowercase in destination)
    • *
    • uuid
    • *
    *

    - * Endianness field: little endian is the default, in this case the field is absent. In case of - * big endian, the field is "Be".
    Bit ordering: Lsb0 is the default, in this case the field - * is absent. In case of Msb0, the field is "Msb0". + * Endianness field: little-endian is the default, in this case the field is absent. In case of big-endian, the field is "Be". + *

    + *

    + * Bit ordering: LSB0 is the default, in this case the field is absent. In case of MSB0, the field is "Msb0" (Camel-case). *

    *

    - * Example: intBeMsb0ToHex convert an int with big endian byte order and Msb0 bit order into its - * hexadecimal string representation + * Example: intBeMsb0ToHex convert an {@code int} with big-endian byte order and MSB0 bit order into its hexadecimal string representation *

    *

    - * Most of the methods provide only default encoding for destination, this limits the number of - * ways to do one thing. Unless you are dealing with data from/to outside of the JVM platform, - * you should not need to use "Be" and "Msb0" methods. + * Most of the methods provide only default encoding for destination, this limits the number of ways to do one thing. Unless you are dealing with data from/to + * outside of the JVM platform, you should not need to use "Be" and "Msb0" methods. *

    *

    - * Development status: work on going, only a part of the little endian, Lsb0 methods implemented - * so far. + * Development status: work on going, only a part of the little-endian, LSB0 methods implemented so far. *

    * * @since 3.2 */ - public class Conversion { - private static final boolean[] TTTT = {true, true, true, true}; - private static final boolean[] FTTT = {false, true, true, true}; - private static final boolean[] TFTT = {true, false, true, true}; - private static final boolean[] FFTT = {false, false, true, true}; - private static final boolean[] TTFT = {true, true, false, true}; - private static final boolean[] FTFT = {false, true, false, true}; - private static final boolean[] TFFT = {true, false, false, true}; - private static final boolean[] FFFT = {false, false, false, true}; - private static final boolean[] TTTF = {true, true, true, false}; - private static final boolean[] FTTF = {false, true, true, false}; - private static final boolean[] TFTF = {true, false, true, false}; - private static final boolean[] FFTF = {false, false, true, false}; - private static final boolean[] TTFF = {true, true, false, false}; - private static final boolean[] FTFF = {false, true, false, false}; - private static final boolean[] TFFF = {true, false, false, false}; - private static final boolean[] FFFF = {false, false, false, false}; + private static final boolean[] TTTT = { true, true, true, true }; + private static final boolean[] FTTT = { false, true, true, true }; + private static final boolean[] TFTT = { true, false, true, true }; + private static final boolean[] FFTT = { false, false, true, true }; + private static final boolean[] TTFT = { true, true, false, true }; + private static final boolean[] FTFT = { false, true, false, true }; + private static final boolean[] TFFT = { true, false, false, true }; + private static final boolean[] FFFT = { false, false, false, true }; + private static final boolean[] TTTF = { true, true, true, false }; + private static final boolean[] FTTF = { false, true, true, false }; + private static final boolean[] TFTF = { true, false, true, false }; + private static final boolean[] FFTF = { false, false, true, false }; + private static final boolean[] TTFF = { true, true, false, false }; + private static final boolean[] FTFF = { false, true, false, false }; + private static final boolean[] TFFF = { true, false, false, false }; + private static final boolean[] FFFF = { false, false, false, false }; /** - *

    - * Converts a hexadecimal digit into an int using the default (Lsb0) bit ordering. - *

    - *

    - * '1' is converted to 1 - *

    + * Converts the first 4 bits of a binary (represented as boolean array) in big-endian MSB0 bit ordering to a hexadecimal digit. * - * @param hexDigit the hexadecimal digit to convert - * @return an int equals to {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit - */ - public static int hexDigitToInt(final char hexDigit) { - final int digit = Character.digit(hexDigit, 16); - if (digit < 0) { - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); - } - return digit; - } - - /** - *

    - * Converts a hexadecimal digit into an int using the Msb0 bit ordering. - *

    *

    - * '1' is converted to 8 + * (1, 0, 0, 0) is converted as follow: '8' (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0) is converted to '4'. *

    * - * @param hexDigit the hexadecimal digit to convert - * @return an int equals to {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit + * @param src The binary to convert. + * @return A hexadecimal digit representing the selected bits. + * @throws IllegalArgumentException if {@code src} is empty. + * @throws NullPointerException if {@code src} is {@code null}. */ - public static int hexDigitMsb0ToInt(final char hexDigit) { - switch (hexDigit) { - case '0': - return 0x0; - case '1': - return 0x8; - case '2': - return 0x4; - case '3': - return 0xC; - case '4': - return 0x2; - case '5': - return 0xA; - case '6': - return 0x6; - case '7': - return 0xE; - case '8': - return 0x1; - case '9': - return 0x9; - case 'a':// fall through - case 'A': - return 0x5; - case 'b':// fall through - case 'B': - return 0xD; - case 'c':// fall through - case 'C': - return 0x3; - case 'd':// fall through - case 'D': - return 0xB; - case 'e':// fall through - case 'E': - return 0x7; - case 'f':// fall through - case 'F': - return 0xF; - default: - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); - } + public static char binaryBeMsb0ToHexDigit(final boolean[] src) { + return binaryBeMsb0ToHexDigit(src, 0); } /** + * Converts a binary (represented as boolean array) in big-endian MSB0 bit ordering to a hexadecimal digit. + * *

    - * Converts a hexadecimal digit into binary (represented as boolean array) using the default - * (Lsb0) bit ordering. - *

    - *

    - * '1' is converted as follow: (1, 0, 0, 0) + * (1, 0, 0, 0) with srcPos = 0 is converted as follow: '8' (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0) with srcPos = 2 is converted to '5'. *

    * - * @param hexDigit the hexadecimal digit to convert - * @return a boolean array with the binary representation of {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit + * @param src The binary to convert. + * @param srcPos The position of the LSB to start the conversion. + * @return A hexadecimal digit representing the selected bits. + * @throws IllegalArgumentException if {@code src} is empty. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IndexOutOfBoundsException if {@code srcPos} is outside the array. */ - public static boolean[] hexDigitToBinary(final char hexDigit) { - switch (hexDigit) { - case '0': - return FFFF.clone(); - case '1': - return TFFF.clone(); - case '2': - return FTFF.clone(); - case '3': - return TTFF.clone(); - case '4': - return FFTF.clone(); - case '5': - return TFTF.clone(); - case '6': - return FTTF.clone(); - case '7': - return TTTF.clone(); - case '8': - return FFFT.clone(); - case '9': - return TFFT.clone(); - case 'a':// fall through - case 'A': - return FTFT.clone(); - case 'b':// fall through - case 'B': - return TTFT.clone(); - case 'c':// fall through - case 'C': - return FFTT.clone(); - case 'd':// fall through - case 'D': - return TFTT.clone(); - case 'e':// fall through - case 'E': - return FTTT.clone(); - case 'f':// fall through - case 'F': - return TTTT.clone(); - default: - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); + public static char binaryBeMsb0ToHexDigit(final boolean[] src, final int srcPos) { + // JDK 9: Objects.checkIndex(int index, int length) + if (Integer.compareUnsigned(srcPos, src.length) >= 0) { + // Throw the correct exception + if (src.length == 0) { + throw new IllegalArgumentException("Cannot convert an empty array."); + } + throw new IndexOutOfBoundsException(srcPos + " is not within array length " + src.length); + } + // Little-endian bit 0 position + final int pos = src.length - 1 - srcPos; + if (3 <= pos && src[pos - 3]) { + if (src[pos - 2]) { + if (src[pos - 1]) { + return src[pos] ? 'f' : 'e'; + } + return src[pos] ? 'd' : 'c'; + } + if (src[pos - 1]) { + return src[pos] ? 'b' : 'a'; + } + return src[pos] ? '9' : '8'; + } + if (2 <= pos && src[pos - 2]) { + if (src[pos - 1]) { + return src[pos] ? '7' : '6'; + } + return src[pos] ? '5' : '4'; + } + if (1 <= pos && src[pos - 1]) { + return src[pos] ? '3' : '2'; } + return src[pos] ? '1' : '0'; } /** - *

    - * Converts a hexadecimal digit into binary (represented as boolean array) using the Msb0 - * bit ordering. - *

    - *

    - * '1' is converted as follow: (0, 0, 0, 1) - *

    + * Converts binary (represented as boolean array) into a byte using the default (little-endian, LSB0) byte and bit ordering. * - * @param hexDigit the hexadecimal digit to convert - * @return a boolean array with the binary representation of {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit + * @param src The binary to convert. + * @param srcPos The position in {@code src}, in boolean unit, from where to start the conversion. + * @param dstInit initial value of the destination byte. + * @param dstPos The position of the LSB, in bits, in the result byte. + * @param nBools The number of booleans to convert. + * @return A byte containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + dstPos >= 8}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length}. */ - public static boolean[] hexDigitMsb0ToBinary(final char hexDigit) { - switch (hexDigit) { - case '0': - return FFFF.clone(); - case '1': - return FFFT.clone(); - case '2': - return FFTF.clone(); - case '3': - return FFTT.clone(); - case '4': - return FTFF.clone(); - case '5': - return FTFT.clone(); - case '6': - return FTTF.clone(); - case '7': - return FTTT.clone(); - case '8': - return TFFF.clone(); - case '9': - return TFFT.clone(); - case 'a':// fall through - case 'A': - return TFTF.clone(); - case 'b':// fall through - case 'B': - return TFTT.clone(); - case 'c':// fall through - case 'C': - return TTFF.clone(); - case 'd':// fall through - case 'D': - return TTFT.clone(); - case 'e':// fall through - case 'E': - return TTTF.clone(); - case 'f':// fall through - case 'F': - return TTTT.clone(); - default: - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); + public static byte binaryToByte(final boolean[] src, final int srcPos, final byte dstInit, final int dstPos, final int nBools) { + if (src.length == 0 && srcPos == 0 || 0 == nBools) { + return dstInit; + } + if ((long) nBools - 1 + dstPos >= Byte.SIZE) { + throw new IllegalArgumentException("nBools - 1 + dstPos >= 8"); + } + byte out = dstInit; + for (int i = 0; i < nBools; i++) { + final int shift = i + dstPos; + final int bits = (src[i + srcPos] ? 1 : 0) << shift; + final int mask = 0x1 << shift; + out = (byte) (out & ~mask | bits); } + return out; } /** + * Converts binary (represented as boolean array) to a hexadecimal digit using the default (LSB0) bit ordering. + * *

    - * Converts binary (represented as boolean array) to a hexadecimal digit using the default - * (Lsb0) bit ordering. - *

    - *

    - * (1, 0, 0, 0) is converted as follow: '1' + * (1, 0, 0, 0) is converted as follow: '1'. *

    * - * @param src the binary to convert - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} + * @param src The binary to convert. + * @return A hexadecimal digit representing the selected bits. + * @throws IllegalArgumentException if {@code src} is empty. + * @throws NullPointerException if {@code src} is {@code null}. */ public static char binaryToHexDigit(final boolean[] src) { return binaryToHexDigit(src, 0); } /** + * Converts binary (represented as boolean array) to a hexadecimal digit using the default (LSB0) bit ordering. + * *

    - * Converts binary (represented as boolean array) to a hexadecimal digit using the default - * (Lsb0) bit ordering. - *

    - *

    - * (1, 0, 0, 0) is converted as follow: '1' + * (1, 0, 0, 0) is converted as follow: '1'. *

    * - * @param src the binary to convert - * @param srcPos the position of the lsb to start the conversion - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} + * @param src The binary to convert. + * @param srcPos The position of the LSB to start the conversion. + * @return A hexadecimal digit representing the selected bits. + * @throws IllegalArgumentException if {@code src} is empty. + * @throws NullPointerException if {@code src} is {@code null}. */ public static char binaryToHexDigit(final boolean[] src, final int srcPos) { if (src.length == 0) { @@ -335,47 +231,40 @@ public static char binaryToHexDigit(final boolean[] src, final int srcPos) { } /** + * Converts binary (represented as boolean array) to a hexadecimal digit using the MSB0 bit ordering. + * *

    - * Converts binary (represented as boolean array) to a hexadecimal digit using the Msb0 bit - * ordering. - *

    - *

    - * (1, 0, 0, 0) is converted as follow: '8' + * (1, 0, 0, 0) is converted as follow: '8'. *

    * - * @param src the binary to convert - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty, {@code src.length < 4} or - * {@code src.length > 8} - * @throws NullPointerException if {@code src} is {@code null} + * @param src The binary to convert. + * @return A hexadecimal digit representing the selected bits. + * @throws IllegalArgumentException if {@code src} is empty, {@code src.length < 4} or {@code src.length > 8}. + * @throws NullPointerException if {@code src} is {@code null}. */ public static char binaryToHexDigitMsb0_4bits(final boolean[] src) { return binaryToHexDigitMsb0_4bits(src, 0); } /** + * Converts binary (represented as boolean array) to a hexadecimal digit using the MSB0 bit ordering. + * *

    - * Converts binary (represented as boolean array) to a hexadecimal digit using the Msb0 bit - * ordering. - *

    - *

    - * (1, 0, 0, 0) is converted as follow: '8' (1, 0, 0, 1, 1, 0, 1, 0) with srcPos = 3 is converted - * to 'D' + * (1, 0, 0, 0) is converted as follow: '8' (1, 0, 0, 1, 1, 0, 1, 0) with srcPos = 3 is converted to 'D' *

    * - * @param src the binary to convert - * @param srcPos the position of the lsb to start the conversion - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty, {@code src.length > 8} or - * {@code src.length - srcPos < 4} - * @throws NullPointerException if {@code src} is {@code null} + * @param src The binary to convert. + * @param srcPos The position of the LSB to start the conversion. + * @return A hexadecimal digit representing the selected bits. + * @throws IllegalArgumentException if {@code src} is empty, {@code src.length > 8} or {@code src.length - srcPos < 4}. + * @throws NullPointerException if {@code src} is {@code null}. */ public static char binaryToHexDigitMsb0_4bits(final boolean[] src, final int srcPos) { - if (src.length > 8) { - throw new IllegalArgumentException("src.length>8: src.length=" + src.length); + if (src.length > Byte.SIZE) { + throw new IllegalArgumentException("src.length > 8: src.length=" + src.length); } if (src.length - srcPos < 4) { - throw new IllegalArgumentException("src.length-srcPos<4: src.length=" + src.length + ", srcPos=" + srcPos); + throw new IllegalArgumentException("src.length - srcPos < 4: src.length=" + src.length + ", srcPos=" + srcPos); } if (src[srcPos + 3]) { if (src[srcPos + 2]) { @@ -402,859 +291,864 @@ public static char binaryToHexDigitMsb0_4bits(final boolean[] src, final int src } /** - *

    - * Converts the first 4 bits of a binary (represented as boolean array) in big endian Msb0 - * bit ordering to a hexadecimal digit. - *

    - *

    - * (1, 0, 0, 0) is converted as follow: '8' (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0) is converted - * to '4' - *

    + * Converts binary (represented as boolean array) into an int using the default (little endian, LSB0) byte and bit ordering. * - * @param src the binary to convert - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} + * @param src The binary to convert. + * @param srcPos The position in {@code src}, in boolean unit, from where to start the conversion. + * @param dstInit initial value of the destination int. + * @param dstPos The position of the LSB, in bits, in the result int. + * @param nBools The number of booleans to convert. + * @return An int containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + dstPos >= 32}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length}. */ - public static char binaryBeMsb0ToHexDigit(final boolean[] src) { - return binaryBeMsb0ToHexDigit(src, 0); + public static int binaryToInt(final boolean[] src, final int srcPos, final int dstInit, final int dstPos, final int nBools) { + if (src.length == 0 && srcPos == 0 || 0 == nBools) { + return dstInit; + } + if ((long) nBools - 1 + dstPos >= Integer.SIZE) { + throw new IllegalArgumentException("nBools - 1 + dstPos >= 32"); + } + int out = dstInit; + for (int i = 0; i < nBools; i++) { + final int shift = i + dstPos; + final int bits = (src[i + srcPos] ? 1 : 0) << shift; + final int mask = 0x1 << shift; + out = out & ~mask | bits; + } + return out; } /** - *

    - * Converts a binary (represented as boolean array) in big endian Msb0 bit ordering to a - * hexadecimal digit. - *

    - *

    - * (1, 0, 0, 0) with srcPos = 0 is converted as follow: '8' (1, 0, 0, 0, 0, 0, 0, 0, - * 0, 0, 0, 1, 0, 1, 0, 0) with srcPos = 2 is converted to '5' - *

    + * Converts binary (represented as boolean array) into a long using the default (little endian, LSB0) byte and bit ordering. * - * @param src the binary to convert - * @param srcPos the position of the lsb to start the conversion - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} + * @param src The binary to convert. + * @param srcPos The position in {@code src}, in boolean unit, from where to start the conversion. + * @param dstInit initial value of the destination long. + * @param dstPos The position of the LSB, in bits, in the result long. + * @param nBools The number of booleans to convert. + * @return A long containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + dstPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length}. */ - public static char binaryBeMsb0ToHexDigit(boolean[] src, int srcPos) { - if (src.length == 0) { - throw new IllegalArgumentException("Cannot convert an empty array."); - } - final int beSrcPos = src.length - 1 - srcPos; - final int srcLen = Math.min(4, beSrcPos + 1); - final boolean[] paddedSrc = new boolean[4]; - System.arraycopy(src, beSrcPos + 1 - srcLen, paddedSrc, 4 - srcLen, srcLen); - src = paddedSrc; - srcPos = 0; - if (src[srcPos]) { - if (src.length > srcPos + 1 && src[srcPos + 1]) { - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? 'f' : 'e'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? 'd' : 'c'; - } - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? 'b' : 'a'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? '9' : '8'; + public static long binaryToLong(final boolean[] src, final int srcPos, final long dstInit, final int dstPos, final int nBools) { + if (src.length == 0 && srcPos == 0 || 0 == nBools) { + return dstInit; } - if (src.length > srcPos + 1 && src[srcPos + 1]) { - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? '7' : '6'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? '5' : '4'; + if ((long) nBools - 1 + dstPos >= Long.SIZE) { + throw new IllegalArgumentException("nBools - 1 + dstPos >= 64"); } - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? '3' : '2'; + long out = dstInit; + for (int i = 0; i < nBools; i++) { + final int shift = i + dstPos; + final long bits = (src[i + srcPos] ? 1L : 0) << shift; + final long mask = 0x1L << shift; + out = out & ~mask | bits; } - return src.length > srcPos + 3 && src[srcPos + 3] ? '1' : '0'; + return out; } /** - *

    - * Converts the 4 lsb of an int to a hexadecimal digit. - *

    - *

    - * 0 returns '0' - *

    - *

    - * 1 returns '1' - *

    - *

    - * 10 returns 'A' and so on... - *

    + * Converts binary (represented as boolean array) into a short using the default (little endian, LSB0) byte and bit ordering. * - * @param nibble the 4 bits to convert - * @return a hexadecimal digit representing the 4 lsb of {@code nibble} - * @throws IllegalArgumentException if {@code nibble < 0} or {@code nibble > 15} + * @param src The binary to convert. + * @param srcPos The position in {@code src}, in boolean unit, from where to start the conversion. + * @param dstInit initial value of the destination short. + * @param dstPos The position of the LSB, in bits, in the result short. + * @param nBools The number of booleans to convert. + * @return A short containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + dstPos >= 16}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length}. */ - public static char intToHexDigit(final int nibble) { - final char c = Character.forDigit(nibble, 16); - if (c == Character.MIN_VALUE) { - throw new IllegalArgumentException("nibble value not between 0 and 15: " + nibble); + public static short binaryToShort(final boolean[] src, final int srcPos, final short dstInit, final int dstPos, final int nBools) { + if (src.length == 0 && srcPos == 0 || 0 == nBools) { + return dstInit; } - return c; + if ((long) nBools - 1 + dstPos >= Short.SIZE) { + throw new IllegalArgumentException("nBools - 1 + dstPos >= 16"); + } + short out = dstInit; + for (int i = 0; i < nBools; i++) { + final int shift = i + dstPos; + final int bits = (src[i + srcPos] ? 1 : 0) << shift; + final int mask = 0x1 << shift; + out = (short) (out & ~mask | bits); + } + return out; } /** - *

    - * Converts the 4 lsb of an int to a hexadecimal digit encoded using the Msb0 bit ordering. - *

    - *

    - * 0 returns '0' - *

    - *

    - * 1 returns '8' - *

    - *

    - * 10 returns '5' and so on... - *

    + * Converts an array of byte into an int using the default (little-endian, LSB0) byte and bit ordering. * - * @param nibble the 4 bits to convert - * @return a hexadecimal digit representing the 4 lsb of {@code nibble} - * @throws IllegalArgumentException if {@code nibble < 0} or {@code nibble > 15} + * @param src The byte array to convert. + * @param srcPos The position in {@code src}, in byte unit, from where to start the conversion. + * @param dstInit initial value of the destination int. + * @param dstPos The position of the LSB, in bits, in the result int. + * @param nBytes The number of bytes to convert. + * @return An int containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code (nBytes - 1) * 8 + dstPos >= 32}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBytes > src.length}. */ - public static char intToHexDigitMsb0(final int nibble) { - switch (nibble) { - case 0x0: - return '0'; - case 0x1: - return '8'; - case 0x2: - return '4'; - case 0x3: - return 'c'; - case 0x4: - return '2'; - case 0x5: - return 'a'; - case 0x6: - return '6'; - case 0x7: - return 'e'; - case 0x8: - return '1'; - case 0x9: - return '9'; - case 0xA: - return '5'; - case 0xB: - return 'd'; - case 0xC: - return '3'; - case 0xD: - return 'b'; - case 0xE: - return '7'; - case 0xF: - return 'f'; - default: - throw new IllegalArgumentException("nibble value not between 0 and 15: " + nibble); + public static int byteArrayToInt(final byte[] src, final int srcPos, final int dstInit, final int dstPos, final int nBytes) { + if (src.length == 0 && srcPos == 0 || 0 == nBytes) { + return dstInit; + } + if (((long) nBytes - 1) * Byte.SIZE + dstPos >= Integer.SIZE) { + throw new IllegalArgumentException("(nBytes - 1) * 8 + dstPos >= 32"); + } + int out = dstInit; + for (int i = 0; i < nBytes; i++) { + final int shift = i * Byte.SIZE + dstPos; + final int bits = (0xff & src[i + srcPos]) << shift; + final int mask = 0xff << shift; + out = out & ~mask | bits; } + return out; } /** - *

    - * Converts an array of int into a long using the default (little endian, Lsb0) byte and bit - * ordering. - *

    + * Converts an array of byte into a long using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the int array to convert - * @param srcPos the position in {@code src}, in int unit, from where to start the - * conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nInts the number of ints to convert - * @return a long containing the selected bits - * @throws IllegalArgumentException if {@code (nInts-1)*32+dstPos >= 64} - * @throws NullPointerException if {@code src} is {@code null} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nInts > src.length} + * @param src The byte array to convert. + * @param srcPos The position in {@code src}, in byte unit, from where to start the conversion. + * @param dstInit initial value of the destination long. + * @param dstPos The position of the LSB, in bits, in the result long. + * @param nBytes The number of bytes to convert. + * @return A long containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code (nBytes - 1) * 8 + dstPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBytes > src.length}. */ - public static long intArrayToLong(final int[] src, final int srcPos, final long dstInit, final int dstPos, - final int nInts) { - if (src.length == 0 && srcPos == 0 || 0 == nInts) { + public static long byteArrayToLong(final byte[] src, final int srcPos, final long dstInit, final int dstPos, final int nBytes) { + if (src.length == 0 && srcPos == 0 || 0 == nBytes) { return dstInit; } - if ((nInts - 1) * 32 + dstPos >= 64) { - throw new IllegalArgumentException("(nInts-1)*32+dstPos is greater or equal to than 64"); + if (((long) nBytes - 1) * Byte.SIZE + dstPos >= Long.SIZE) { + throw new IllegalArgumentException("(nBytes - 1) * 8 + dstPos >= 64"); } long out = dstInit; - for (int i = 0; i < nInts; i++) { - final int shift = i * 32 + dstPos; - final long bits = (0xffffffffL & src[i + srcPos]) << shift; - final long mask = 0xffffffffL << shift; - out = (out & ~mask) | bits; + for (int i = 0; i < nBytes; i++) { + final int shift = i * Byte.SIZE + dstPos; + final long bits = (0xffL & src[i + srcPos]) << shift; + final long mask = 0xffL << shift; + out = out & ~mask | bits; } return out; } /** - *

    - * Converts an array of short into a long using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts an array of byte into a short using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the short array to convert - * @param srcPos the position in {@code src}, in short unit, from where to start the - * conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nShorts the number of shorts to convert - * @return a long containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+dstPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nShorts > src.length} + * @param src The byte array to convert. + * @param srcPos The position in {@code src}, in byte unit, from where to start the conversion. + * @param dstInit initial value of the destination short. + * @param dstPos The position of the LSB, in bits, in the result short. + * @param nBytes The number of bytes to convert. + * @return A short containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code (nBytes - 1) * 8 + dstPos >= 16}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBytes > src.length}. */ - public static long shortArrayToLong(final short[] src, final int srcPos, final long dstInit, final int dstPos, - final int nShorts) { - if (src.length == 0 && srcPos == 0 || 0 == nShorts) { + public static short byteArrayToShort(final byte[] src, final int srcPos, final short dstInit, final int dstPos, final int nBytes) { + if (src.length == 0 && srcPos == 0 || 0 == nBytes) { return dstInit; } - if ((nShorts - 1) * 16 + dstPos >= 64) { - throw new IllegalArgumentException("(nShorts-1)*16+dstPos is greater or equal to than 64"); + if (((long) nBytes - 1) * Byte.SIZE + dstPos >= Short.SIZE) { + throw new IllegalArgumentException("(nBytes - 1) * 8 + dstPos >= 16"); } - long out = dstInit; - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + dstPos; - final long bits = (0xffffL & src[i + srcPos]) << shift; - final long mask = 0xffffL << shift; - out = (out & ~mask) | bits; + short out = dstInit; + for (int i = 0; i < nBytes; i++) { + final int shift = i * Byte.SIZE + dstPos; + final int bits = (0xff & src[i + srcPos]) << shift; + final int mask = 0xff << shift; + out = (short) (out & ~mask | bits); } return out; } /** - *

    - * Converts an array of short into an int using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts bytes from an array into a UUID using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the short array to convert - * @param srcPos the position in {@code src}, in short unit, from where to start the - * conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nShorts the number of shorts to convert - * @return an int containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+dstPos >= 32} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nShorts > src.length} + * @param src The byte array to convert. + * @param srcPos The position in {@code src} where to copy the result from. + * @return A UUID. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if array does not contain at least 16 bytes beginning with {@code srcPos}. */ - public static int shortArrayToInt(final short[] src, final int srcPos, final int dstInit, final int dstPos, - final int nShorts) { - if (src.length == 0 && srcPos == 0 || 0 == nShorts) { - return dstInit; - } - if ((nShorts - 1) * 16 + dstPos >= 32) { - throw new IllegalArgumentException("(nShorts-1)*16+dstPos is greater or equal to than 32"); - } - int out = dstInit; - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + dstPos; - final int bits = (0xffff & src[i + srcPos]) << shift; - final int mask = 0xffff << shift; - out = (out & ~mask) | bits; + public static UUID byteArrayToUuid(final byte[] src, final int srcPos) { + if (src.length - srcPos < 16) { + throw new IllegalArgumentException("Need at least 16 bytes for UUID"); } - return out; + return new UUID(byteArrayToLong(src, srcPos, 0, 0, Byte.SIZE), byteArrayToLong(src, srcPos + 8, 0, 0, Byte.SIZE)); } /** - *

    - * Converts an array of byte into a long using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts a byte into an array of boolean using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the byte array to convert - * @param srcPos the position in {@code src}, in byte unit, from where to start the - * conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nBytes the number of bytes to convert - * @return a long containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+dstPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBytes > src.length} + * @param src The byte to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBools The number of booleans to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + srcPos >= 8}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length}. */ - public static long byteArrayToLong(final byte[] src, final int srcPos, final long dstInit, final int dstPos, - final int nBytes) { - if (src.length == 0 && srcPos == 0 || 0 == nBytes) { - return dstInit; + public static boolean[] byteToBinary(final byte src, final int srcPos, final boolean[] dst, final int dstPos, final int nBools) { + if (0 == nBools) { + return dst; } - if ((nBytes - 1) * 8 + dstPos >= 64) { - throw new IllegalArgumentException("(nBytes-1)*8+dstPos is greater or equal to than 64"); + if ((long) nBools - 1 + srcPos >= Byte.SIZE) { + throw new IllegalArgumentException("nBools - 1 + srcPos >= 8"); } - long out = dstInit; - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + dstPos; - final long bits = (0xffL & src[i + srcPos]) << shift; - final long mask = 0xffL << shift; - out = (out & ~mask) | bits; + for (int i = 0; i < nBools; i++) { + final int shift = i + srcPos; + dst[dstPos + i] = (0x1 & src >> shift) != 0; } - return out; + return dst; } /** - *

    - * Converts an array of byte into an int using the default (little endian, Lsb0) byte and bit - * ordering. - *

    + * Converts a byte into an array of char using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the byte array to convert - * @param srcPos the position in {@code src}, in byte unit, from where to start the - * conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nBytes the number of bytes to convert - * @return an int containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+dstPos >= 32} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBytes > src.length} + * @param src The byte to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dstInit The initial value for the result String. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nHexs The number of chars to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + srcPos >= 8}. + * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos}. */ - public static int byteArrayToInt(final byte[] src, final int srcPos, final int dstInit, final int dstPos, - final int nBytes) { - if (src.length == 0 && srcPos == 0 || 0 == nBytes) { + public static String byteToHex(final byte src, final int srcPos, final String dstInit, final int dstPos, final int nHexs) { + if (0 == nHexs) { return dstInit; } - if ((nBytes - 1) * 8 + dstPos >= 32) { - throw new IllegalArgumentException("(nBytes-1)*8+dstPos is greater or equal to than 32"); + if (((long) nHexs - 1) * 4 + srcPos >= Byte.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + srcPos >= 8"); } - int out = dstInit; - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + dstPos; - final int bits = (0xff & src[i + srcPos]) << shift; - final int mask = 0xff << shift; - out = (out & ~mask) | bits; + final StringBuilder sb = new StringBuilder(dstInit); + int append = sb.length(); + for (int i = 0; i < nHexs; i++) { + final int shift = i * 4 + srcPos; + final int bits = 0xF & src >> shift; + if (dstPos + i == append) { + ++append; + sb.append(intToHexDigit(bits)); + } else { + sb.setCharAt(dstPos + i, intToHexDigit(bits)); + } } - return out; + return sb.toString(); } /** + * Converts a hexadecimal digit into binary (represented as boolean array) using the MSB0 bit ordering. + * *

    - * Converts an array of byte into a short using the default (little endian, Lsb0) byte and - * bit ordering. + * '1' is converted as follow: (0, 0, 0, 1). *

    * - * @param src the byte array to convert - * @param srcPos the position in {@code src}, in byte unit, from where to start the - * conversion - * @param dstInit initial value of the destination short - * @param dstPos the position of the lsb, in bits, in the result short - * @param nBytes the number of bytes to convert - * @return a short containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+dstPos >= 16} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBytes > src.length} + * @param hexChar The hexadecimal digit to convert. + * @return A boolean array with the binary representation of {@code hexDigit}. + * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit. */ - public static short byteArrayToShort(final byte[] src, final int srcPos, final short dstInit, final int dstPos, - final int nBytes) { - if (src.length == 0 && srcPos == 0 || 0 == nBytes) { - return dstInit; - } - if ((nBytes - 1) * 8 + dstPos >= 16) { - throw new IllegalArgumentException("(nBytes-1)*8+dstPos is greater or equal to than 16"); - } - short out = dstInit; - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + dstPos; - final int bits = (0xff & src[i + srcPos]) << shift; - final int mask = 0xff << shift; - out = (short) ((out & ~mask) | bits); + public static boolean[] hexDigitMsb0ToBinary(final char hexChar) { + switch (hexChar) { + case '0': + return FFFF.clone(); + case '1': + return FFFT.clone(); + case '2': + return FFTF.clone(); + case '3': + return FFTT.clone(); + case '4': + return FTFF.clone(); + case '5': + return FTFT.clone(); + case '6': + return FTTF.clone(); + case '7': + return FTTT.clone(); + case '8': + return TFFF.clone(); + case '9': + return TFFT.clone(); + case 'a':// fall through + case 'A': + return TFTF.clone(); + case 'b':// fall through + case 'B': + return TFTT.clone(); + case 'c':// fall through + case 'C': + return TTFF.clone(); + case 'd':// fall through + case 'D': + return TTFT.clone(); + case 'e':// fall through + case 'E': + return TTTF.clone(); + case 'f':// fall through + case 'F': + return TTTT.clone(); + default: + throw new IllegalArgumentException("Cannot convert '" + hexChar + "' to a hexadecimal digit"); } - return out; } /** + * Converts a hexadecimal digit into an int using the MSB0 bit ordering. + * *

    - * Converts an array of Char into a long using the default (little endian, Lsb0) byte and - * bit ordering. + * '1' is converted to 8. *

    * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start the - * conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nHex the number of Chars to convert - * @return a long containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 64} + * @param hexChar The hexadecimal digit to convert. + * @return An int equals to {@code hexDigit}. + * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit. */ - public static long hexToLong(final String src, final int srcPos, final long dstInit, final int dstPos, - final int nHex) { - if (0 == nHex) { - return dstInit; - } - if ((nHex - 1) * 4 + dstPos >= 64) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 64"); - } - long out = dstInit; - for (int i = 0; i < nHex; i++) { - final int shift = i * 4 + dstPos; - final long bits = (0xfL & hexDigitToInt(src.charAt(i + srcPos))) << shift; - final long mask = 0xfL << shift; - out = (out & ~mask) | bits; + public static int hexDigitMsb0ToInt(final char hexChar) { + switch (hexChar) { + case '0': + return 0x0; + case '1': + return 0x8; + case '2': + return 0x4; + case '3': + return 0xC; + case '4': + return 0x2; + case '5': + return 0xA; + case '6': + return 0x6; + case '7': + return 0xE; + case '8': + return 0x1; + case '9': + return 0x9; + case 'a':// fall through + case 'A': + return 0x5; + case 'b':// fall through + case 'B': + return 0xD; + case 'c':// fall through + case 'C': + return 0x3; + case 'd':// fall through + case 'D': + return 0xB; + case 'e':// fall through + case 'E': + return 0x7; + case 'f':// fall through + case 'F': + return 0xF; + default: + throw new IllegalArgumentException("Cannot convert '" + hexChar + "' to a hexadecimal digit"); } - return out; } /** + * Converts a hexadecimal digit into binary (represented as boolean array) using the default (LSB0) bit ordering. + * *

    - * Converts an array of Char into an int using the default (little endian, Lsb0) byte and bit - * ordering. + * '1' is converted as follow: (1, 0, 0, 0). *

    * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start the - * conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nHex the number of Chars to convert - * @return an int containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 32} + * @param hexChar The hexadecimal digit to convert. + * @return A boolean array with the binary representation of {@code hexDigit}. + * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit. */ - public static int hexToInt(final String src, final int srcPos, final int dstInit, final int dstPos, final int nHex) { - if (0 == nHex) { - return dstInit; - } - if ((nHex - 1) * 4 + dstPos >= 32) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 32"); - } - int out = dstInit; - for (int i = 0; i < nHex; i++) { - final int shift = i * 4 + dstPos; - final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; - final int mask = 0xf << shift; - out = (out & ~mask) | bits; + public static boolean[] hexDigitToBinary(final char hexChar) { + switch (hexChar) { + case '0': + return FFFF.clone(); + case '1': + return TFFF.clone(); + case '2': + return FTFF.clone(); + case '3': + return TTFF.clone(); + case '4': + return FFTF.clone(); + case '5': + return TFTF.clone(); + case '6': + return FTTF.clone(); + case '7': + return TTTF.clone(); + case '8': + return FFFT.clone(); + case '9': + return TFFT.clone(); + case 'a':// fall through + case 'A': + return FTFT.clone(); + case 'b':// fall through + case 'B': + return TTFT.clone(); + case 'c':// fall through + case 'C': + return FFTT.clone(); + case 'd':// fall through + case 'D': + return TFTT.clone(); + case 'e':// fall through + case 'E': + return FTTT.clone(); + case 'f':// fall through + case 'F': + return TTTT.clone(); + default: + throw new IllegalArgumentException("Cannot convert '" + hexChar + "' to a hexadecimal digit"); } - return out; } /** + * Converts a hexadecimal digit into an int using the default (LSB0) bit ordering. + * *

    - * Converts an array of Char into a short using the default (little endian, Lsb0) byte and - * bit ordering. + * '1' is converted to 1. *

    * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start the - * conversion - * @param dstInit initial value of the destination short - * @param dstPos the position of the lsb, in bits, in the result short - * @param nHex the number of Chars to convert - * @return a short containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 16} + * @param hexChar The hexadecimal digit to convert. + * @return An int equals to {@code hexDigit}. + * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal digit. + */ + public static int hexDigitToInt(final char hexChar) { + if (!CharUtils.isHex(hexChar)) { + throw new IllegalArgumentException("Cannot convert '" + hexChar + "' to a hexadecimal digit"); + } + return Character.digit(hexChar, 16); + } + + /** + * Converts a hexadecimal string into a byte using the default (little-endian, LSB0) byte and bit ordering. + * + * @param src The hexadecimal string to convert. + * @param srcPos The position in {@code src}, in char unit, from where to start the conversion. + * @param dstInit initial value of the destination byte. + * @param dstPos The position of the LSB, in bits, in the result byte. + * @param nHex The number of Chars to convert. + * @return A byte containing the selected bits. + * @throws IllegalArgumentException if {@code (nHex-1)*4+dstPos >= 8}. */ - public static short hexToShort(final String src, final int srcPos, final short dstInit, final int dstPos, - final int nHex) { + public static byte hexToByte(final String src, final int srcPos, final byte dstInit, final int dstPos, final int nHex) { if (0 == nHex) { return dstInit; } - if ((nHex - 1) * 4 + dstPos >= 16) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 16"); + if (((long) nHex - 1) * 4 + dstPos >= Byte.SIZE) { + throw new IllegalArgumentException("(nHex - 1) * 4 + dstPos >= 8"); } - short out = dstInit; + byte out = dstInit; for (int i = 0; i < nHex; i++) { final int shift = i * 4 + dstPos; final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; final int mask = 0xf << shift; - out = (short) ((out & ~mask) | bits); + out = (byte) (out & ~mask | bits); } return out; } /** - *

    - * Converts an array of Char into a byte using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts an array of char into an int using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start the - * conversion - * @param dstInit initial value of the destination byte - * @param dstPos the position of the lsb, in bits, in the result byte - * @param nHex the number of Chars to convert - * @return a byte containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 8} + * @param src The hexadecimal string to convert. + * @param srcPos The position in {@code src}, in char unit, from where to start the conversion. + * @param dstInit initial value of the destination int. + * @param dstPos The position of the LSB, in bits, in the result int. + * @param nHex The number of chars to convert. + * @return An int containing the selected bits. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + dstPos >= 32}. */ - public static byte hexToByte(final String src, final int srcPos, final byte dstInit, final int dstPos, - final int nHex) { + public static int hexToInt(final String src, final int srcPos, final int dstInit, final int dstPos, final int nHex) { if (0 == nHex) { return dstInit; } - if ((nHex - 1) * 4 + dstPos >= 8) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 8"); + if (((long) nHex - 1) * 4 + dstPos >= Integer.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + dstPos >= 32"); } - byte out = dstInit; + int out = dstInit; for (int i = 0; i < nHex; i++) { final int shift = i * 4 + dstPos; final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; final int mask = 0xf << shift; - out = (byte) ((out & ~mask) | bits); + out = out & ~mask | bits; } return out; } /** - *

    - * Converts binary (represented as boolean array) into a long using the default (little - * endian, Lsb0) byte and bit ordering. - *

    + * Converts an array of char into a long using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to start the - * conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nBools the number of booleans to convert - * @return a long containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length} + * @param src The hexadecimal string to convert. + * @param srcPos The position in {@code src}, in char unit, from where to start the conversion. + * @param dstInit initial value of the destination long. + * @param dstPos The position of the LSB, in bits, in the result long. + * @param nHex The number of chars to convert. + * @return A long containing the selected bits. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + dstPos >= 64}. */ - public static long binaryToLong(final boolean[] src, final int srcPos, final long dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { + public static long hexToLong(final String src, final int srcPos, final long dstInit, final int dstPos, final int nHex) { + if (0 == nHex) { return dstInit; } - if (nBools - 1 + dstPos >= 64) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 64"); + if (((long) nHex - 1) * 4 + dstPos >= Long.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + dstPos >= 64"); } long out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final long bits = (src[i + srcPos] ? 1L : 0) << shift; - final long mask = 0x1L << shift; - out = (out & ~mask) | bits; + for (int i = 0; i < nHex; i++) { + final int shift = i * 4 + dstPos; + final long bits = (0xfL & hexDigitToInt(src.charAt(i + srcPos))) << shift; + final long mask = 0xfL << shift; + out = out & ~mask | bits; } return out; } /** - *

    - * Converts binary (represented as boolean array) into an int using the default (little - * endian, Lsb0) byte and bit ordering. - *

    + * Converts an array of char into a short using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to start the - * conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nBools the number of booleans to convert - * @return an int containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 32} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length} + * @param src The hexadecimal string to convert. + * @param srcPos The position in {@code src}, in char unit, from where to start the conversion. + * @param dstInit initial value of the destination short. + * @param dstPos The position of the LSB, in bits, in the result short. + * @param nHex The number of chars to convert. + * @return A short containing the selected bits. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + dstPos >= 16}. */ - public static int binaryToInt(final boolean[] src, final int srcPos, final int dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { + public static short hexToShort(final String src, final int srcPos, final short dstInit, final int dstPos, final int nHex) { + if (0 == nHex) { return dstInit; } - if (nBools - 1 + dstPos >= 32) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 32"); + if (((long) nHex - 1) * 4 + dstPos >= Short.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + dstPos >= 16"); } - int out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final int bits = (src[i + srcPos] ? 1 : 0) << shift; - final int mask = 0x1 << shift; - out = (out & ~mask) | bits; + short out = dstInit; + for (int i = 0; i < nHex; i++) { + final int shift = i * 4 + dstPos; + final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; + final int mask = 0xf << shift; + out = (short) (out & ~mask | bits); } return out; } /** - *

    - * Converts binary (represented as boolean array) into a short using the default (little - * endian, Lsb0) byte and bit ordering. - *

    + * Converts an array of int into a long using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to start the - * conversion - * @param dstInit initial value of the destination short - * @param dstPos the position of the lsb, in bits, in the result short - * @param nBools the number of booleans to convert - * @return a short containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 16} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length} + * @param src The int array to convert. + * @param srcPos The position in {@code src}, in int unit, from where to start the conversion. + * @param dstInit initial value of the destination long. + * @param dstPos The position of the LSB, in bits, in the result long. + * @param nInts The number of ints to convert. + * @return A long containing the selected bits. + * @throws IllegalArgumentException if {@code (nInts - 1) * 32 + dstPos >= 64}. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nInts > src.length}. */ - public static short binaryToShort(final boolean[] src, final int srcPos, final short dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { + public static long intArrayToLong(final int[] src, final int srcPos, final long dstInit, final int dstPos, final int nInts) { + if (src.length == 0 && srcPos == 0 || 0 == nInts) { return dstInit; } - if (nBools - 1 + dstPos >= 16) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 16"); + if (((long) nInts - 1) * Integer.SIZE + dstPos >= Long.SIZE) { + throw new IllegalArgumentException("(nInts - 1) * 32 + dstPos >= 64"); } - short out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final int bits = (src[i + srcPos] ? 1 : 0) << shift; - final int mask = 0x1 << shift; - out = (short) ((out & ~mask) | bits); + long out = dstInit; + for (int i = 0; i < nInts; i++) { + final int shift = i * Integer.SIZE + dstPos; + final long bits = (0xffffffffL & src[i + srcPos]) << shift; + final long mask = 0xffffffffL << shift; + out = out & ~mask | bits; } return out; } /** - *

    - * Converts binary (represented as boolean array) into a byte using the default (little - * endian, Lsb0) byte and bit ordering. - *

    + * Converts an int into an array of boolean using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to start the - * conversion - * @param dstInit initial value of the destination byte - * @param dstPos the position of the lsb, in bits, in the result byte - * @param nBools the number of booleans to convert - * @return a byte containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 8} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nBools > src.length} + * @param src The int to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBools The number of booleans to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + srcPos >= 32}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length}. */ - public static byte binaryToByte(final boolean[] src, final int srcPos, final byte dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { - return dstInit; + public static boolean[] intToBinary(final int src, final int srcPos, final boolean[] dst, final int dstPos, final int nBools) { + if (0 == nBools) { + return dst; } - if (nBools - 1 + dstPos >= 8) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 8"); + if ((long) nBools - 1 + srcPos >= Integer.SIZE) { + throw new IllegalArgumentException("nBools - 1 + srcPos >= 32"); } - byte out = dstInit; for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final int bits = (src[i + srcPos] ? 1 : 0) << shift; - final int mask = 0x1 << shift; - out = (byte) ((out & ~mask) | bits); + final int shift = i + srcPos; + dst[dstPos + i] = (0x1 & src >> shift) != 0; } - return out; + return dst; } /** - *

    - * Converts a long into an array of int using the default (little endian, Lsb0) byte and bit - * ordering. - *

    + * Converts an int into an array of byte using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nInts the number of ints to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} and {@code nInts > 0} - * @throws IllegalArgumentException if {@code (nInts-1)*32+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nInts > dst.length} + * @param src The int to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBytes The number of bytes to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code (nBytes - 1) * 8 + srcPos >= 32}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length}. */ - public static int[] longToIntArray(final long src, final int srcPos, final int[] dst, final int dstPos, - final int nInts) { - if (0 == nInts) { + public static byte[] intToByteArray(final int src, final int srcPos, final byte[] dst, final int dstPos, final int nBytes) { + if (0 == nBytes) { return dst; } - if ((nInts - 1) * 32 + srcPos >= 64) { - throw new IllegalArgumentException("(nInts-1)*32+srcPos is greater or equal to than 64"); + if (((long) nBytes - 1) * Byte.SIZE + srcPos >= Integer.SIZE) { + throw new IllegalArgumentException("(nBytes - 1) * 8 + srcPos >= 32"); } - for (int i = 0; i < nInts; i++) { - final int shift = i * 32 + srcPos; - dst[dstPos + i] = (int) (0xffffffff & (src >> shift)); + for (int i = 0; i < nBytes; i++) { + final int shift = i * Byte.SIZE + srcPos; + dst[dstPos + i] = (byte) (0xff & src >> shift); } return dst; } /** - *

    - * Converts a long into an array of short using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts an int into an array of char using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nShorts the number of shorts to copy to {@code dst}, must be smaller or equal to - * the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nShorts > dst.length} + * @param src The int to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dstInit The initial value for the result String. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nHexs The number of chars to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + srcPos >= 32}. + * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos}. */ - public static short[] longToShortArray(final long src, final int srcPos, final short[] dst, final int dstPos, - final int nShorts) { - if (0 == nShorts) { - return dst; + public static String intToHex(final int src, final int srcPos, final String dstInit, final int dstPos, final int nHexs) { + if (0 == nHexs) { + return dstInit; } - if ((nShorts - 1) * 16 + srcPos >= 64) { - throw new IllegalArgumentException("(nShorts-1)*16+srcPos is greater or equal to than 64"); + if (((long) nHexs - 1) * 4 + srcPos >= Integer.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + srcPos >= 32"); } - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + srcPos; - dst[dstPos + i] = (short) (0xffff & (src >> shift)); + final StringBuilder sb = new StringBuilder(dstInit); + int append = sb.length(); + for (int i = 0; i < nHexs; i++) { + final int shift = i * 4 + srcPos; + final int bits = 0xF & src >> shift; + if (dstPos + i == append) { + ++append; + sb.append(intToHexDigit(bits)); + } else { + sb.setCharAt(dstPos + i, intToHexDigit(bits)); + } } - return dst; + return sb.toString(); } /** + * Converts the 4 LSB of an int to a hexadecimal digit. + * *

    - * Converts an int into an array of short using the default (little endian, Lsb0) byte and - * bit ordering. + * 0 returns '0' + *

    + *

    + * 1 returns '1' + *

    + *

    + * 10 returns 'A' and so on... *

    * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nShorts the number of shorts to copy to {@code dst}, must be smaller or equal to - * the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+srcPos >= 32} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nShorts > dst.length} + * @param nibble The 4 bits to convert. + * @return A hexadecimal digit representing the 4 LSB of {@code nibble}. + * @throws IllegalArgumentException if {@code nibble < 0} or {@code nibble > 15}. */ - public static short[] intToShortArray(final int src, final int srcPos, final short[] dst, final int dstPos, - final int nShorts) { - if (0 == nShorts) { - return dst; - } - if ((nShorts - 1) * 16 + srcPos >= 32) { - throw new IllegalArgumentException("(nShorts-1)*16+srcPos is greater or equal to than 32"); - } - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + srcPos; - dst[dstPos + i] = (short) (0xffff & (src >> shift)); + public static char intToHexDigit(final int nibble) { + final char c = Character.forDigit(nibble, 16); + if (c == Character.MIN_VALUE) { + throw new IllegalArgumentException("nibble value not between 0 and 15: " + nibble); } - return dst; + return c; } /** + * Converts the 4 LSB of an int to a hexadecimal digit encoded using the MSB0 bit ordering. + * + *

    + * 0 returns '0' + *

    + *

    + * 1 returns '8' + *

    *

    - * Converts a long into an array of byte using the default (little endian, Lsb0) byte and - * bit ordering. + * 10 returns '5' and so on... *

    * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length} + * @param nibble The 4 bits to convert. + * @return A hexadecimal digit representing the 4 LSB of {@code nibble}. + * @throws IllegalArgumentException if {@code nibble < 0} or {@code nibble > 15}. */ - public static byte[] longToByteArray(final long src, final int srcPos, final byte[] dst, final int dstPos, - final int nBytes) { - if (0 == nBytes) { + public static char intToHexDigitMsb0(final int nibble) { + switch (nibble) { + case 0x0: + return '0'; + case 0x1: + return '8'; + case 0x2: + return '4'; + case 0x3: + return 'c'; + case 0x4: + return '2'; + case 0x5: + return 'a'; + case 0x6: + return '6'; + case 0x7: + return 'e'; + case 0x8: + return '1'; + case 0x9: + return '9'; + case 0xA: + return '5'; + case 0xB: + return 'd'; + case 0xC: + return '3'; + case 0xD: + return 'b'; + case 0xE: + return '7'; + case 0xF: + return 'f'; + default: + throw new IllegalArgumentException("nibble value not between 0 and 15: " + nibble); + } + } + + /** + * Converts an int into an array of short using the default (little-endian, LSB0) byte and bit ordering. + * + * @param src The int to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nShorts The number of shorts to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code (nShorts - 1) * 16 + srcPos >= 32}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nShorts > dst.length}. + */ + public static short[] intToShortArray(final int src, final int srcPos, final short[] dst, final int dstPos, final int nShorts) { + if (0 == nShorts) { return dst; } - if ((nBytes - 1) * 8 + srcPos >= 64) { - throw new IllegalArgumentException("(nBytes-1)*8+srcPos is greater or equal to than 64"); + if (((long) nShorts - 1) * Short.SIZE + srcPos >= Integer.SIZE) { + throw new IllegalArgumentException("(nShorts - 1) * 16 + srcPos >= 32"); } - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + srcPos; - dst[dstPos + i] = (byte) (0xff & (src >> shift)); + for (int i = 0; i < nShorts; i++) { + final int shift = i * Short.SIZE + srcPos; + dst[dstPos + i] = (short) (0xffff & src >> shift); } return dst; } /** - *

    - * Converts an int into an array of byte using the default (little endian, Lsb0) byte and bit - * ordering. - *

    + * Converts a long into an array of boolean using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+srcPos >= 32} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length} + * @param src The long to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBools The number of booleans to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + srcPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length}. */ - public static byte[] intToByteArray(final int src, final int srcPos, final byte[] dst, final int dstPos, - final int nBytes) { - if (0 == nBytes) { + public static boolean[] longToBinary(final long src, final int srcPos, final boolean[] dst, final int dstPos, final int nBools) { + if (0 == nBools) { return dst; } - if ((nBytes - 1) * 8 + srcPos >= 32) { - throw new IllegalArgumentException("(nBytes-1)*8+srcPos is greater or equal to than 32"); + if ((long) nBools - 1 + srcPos >= Long.SIZE) { + throw new IllegalArgumentException("nBools - 1 + srcPos >= 64"); } - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + srcPos; - dst[dstPos + i] = (byte) (0xff & (src >> shift)); + for (int i = 0; i < nBools; i++) { + final int shift = i + srcPos; + dst[dstPos + i] = (0x1 & src >> shift) != 0; } return dst; } /** - *

    - * Converts a short into an array of byte using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts a long into an array of byte using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the short to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+srcPos >= 16} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length} + * @param src The long to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBytes The number of bytes to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code (nBytes - 1) * 8 + srcPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length}. */ - public static byte[] shortToByteArray(final short src, final int srcPos, final byte[] dst, final int dstPos, - final int nBytes) { + public static byte[] longToByteArray(final long src, final int srcPos, final byte[] dst, final int dstPos, final int nBytes) { if (0 == nBytes) { return dst; } - if ((nBytes - 1) * 8 + srcPos >= 16) { - throw new IllegalArgumentException("(nBytes-1)*8+srcPos is greater or equal to than 16"); + if (((long) nBytes - 1) * Byte.SIZE + srcPos >= Long.SIZE) { + throw new IllegalArgumentException("(nBytes - 1) * 8 + srcPos >= 64"); } for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + srcPos; - dst[dstPos + i] = (byte) (0xff & (src >> shift)); + final int shift = i * Byte.SIZE + srcPos; + dst[dstPos + i] = (byte) (0xff & src >> shift); } return dst; } /** - *

    - * Converts a long into an array of Char using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts a long into an array of char using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 64} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} + * @param src The long to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dstInit The initial value for the result String. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nHexs The number of chars to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + srcPos >= 64}. + * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos}. */ - public static String longToHex(final long src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { + public static String longToHex(final long src, final int srcPos, final String dstInit, final int dstPos, final int nHexs) { if (0 == nHexs) { return dstInit; } - if ((nHexs - 1) * 4 + srcPos >= 64) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 64"); + if (((long) nHexs - 1) * 4 + srcPos >= Long.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + srcPos >= 64"); } final StringBuilder sb = new StringBuilder(dstInit); int append = sb.length(); for (int i = 0; i < nHexs; i++) { final int shift = i * 4 + srcPos; - final int bits = (int) (0xF & (src >> shift)); + final int bits = (int) (0xF & src >> shift); if (dstPos + i == append) { ++append; sb.append(intToHexDigit(bits)); @@ -1266,275 +1160,228 @@ public static String longToHex(final long src, final int srcPos, final String ds } /** - *

    - * Converts an int into an array of Char using the default (little endian, Lsb0) byte and bit - * ordering. - *

    + * Converts a long into an array of int using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 32} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} + * @param src The long to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nInts The number of ints to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null} and {@code nInts > 0}. + * @throws IllegalArgumentException if {@code (nInts - 1) * 32 + srcPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nInts > dst.length}. */ - public static String intToHex(final int src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { - return dstInit; + public static int[] longToIntArray(final long src, final int srcPos, final int[] dst, final int dstPos, final int nInts) { + if (0 == nInts) { + return dst; } - if ((nHexs - 1) * 4 + srcPos >= 32) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 32"); + if (((long) nInts - 1) * Integer.SIZE + srcPos >= Long.SIZE) { + throw new IllegalArgumentException("(nInts - 1) * 32 + srcPos >= 64"); } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = 0xF & (src >> shift); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } + for (int i = 0; i < nInts; i++) { + final int shift = i * Integer.SIZE + srcPos; + dst[dstPos + i] = (int) (0xffffffff & src >> shift); } - return sb.toString(); + return dst; } /** - *

    - * Converts a short into an array of Char using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts a long into an array of short using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the short to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 16} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} + * @param src The long to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nShorts The number of shorts to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code (nShorts - 1) * 16 + srcPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nShorts > dst.length}. */ - public static String shortToHex(final short src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { - return dstInit; + public static short[] longToShortArray(final long src, final int srcPos, final short[] dst, final int dstPos, final int nShorts) { + if (0 == nShorts) { + return dst; } - if ((nHexs - 1) * 4 + srcPos >= 16) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 16"); + if (((long) nShorts - 1) * Short.SIZE + srcPos >= Long.SIZE) { + throw new IllegalArgumentException("(nShorts - 1) * 16 + srcPos >= 64"); } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = 0xF & (src >> shift); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } + for (int i = 0; i < nShorts; i++) { + final int shift = i * Short.SIZE + srcPos; + dst[dstPos + i] = (short) (0xffff & src >> shift); } - return sb.toString(); + return dst; } /** - *

    - * Converts a byte into an array of Char using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts an array of short into an int using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the byte to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 8} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} + * @param src The short array to convert. + * @param srcPos The position in {@code src}, in short unit, from where to start the conversion. + * @param dstInit initial value of the destination int. + * @param dstPos The position of the LSB, in bits, in the result int. + * @param nShorts The number of shorts to convert. + * @return An int containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code (nShorts - 1) * 16 + dstPos >= 32}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nShorts > src.length}. */ - public static String byteToHex(final byte src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { + public static int shortArrayToInt(final short[] src, final int srcPos, final int dstInit, final int dstPos, final int nShorts) { + if (src.length == 0 && srcPos == 0 || 0 == nShorts) { return dstInit; } - if ((nHexs - 1) * 4 + srcPos >= 8) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 8"); + if (((long) nShorts - 1) * Short.SIZE + dstPos >= Integer.SIZE) { + throw new IllegalArgumentException("(nShorts - 1) * 16 + dstPos >= 32"); } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = 0xF & (src >> shift); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } + int out = dstInit; + for (int i = 0; i < nShorts; i++) { + final int shift = i * Short.SIZE + dstPos; + final int bits = (0xffff & src[i + srcPos]) << shift; + final int mask = 0xffff << shift; + out = out & ~mask | bits; } - return sb.toString(); + return out; } /** - *

    - * Converts a long into an array of boolean using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts an array of short into a long using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller or equal to - * the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length} + * @param src The short array to convert. + * @param srcPos The position in {@code src}, in short unit, from where to start the conversion. + * @param dstInit initial value of the destination long. + * @param dstPos The position of the LSB, in bits, in the result long. + * @param nShorts The number of shorts to convert. + * @return A long containing the selected bits. + * @throws NullPointerException if {@code src} is {@code null}. + * @throws IllegalArgumentException if {@code (nShorts - 1) * 16 + dstPos >= 64}. + * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nShorts > src.length}. */ - public static boolean[] longToBinary(final long src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { - return dst; + public static long shortArrayToLong(final short[] src, final int srcPos, final long dstInit, final int dstPos, final int nShorts) { + if (src.length == 0 && srcPos == 0 || 0 == nShorts) { + return dstInit; } - if (nBools - 1 + srcPos >= 64) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 64"); + if (((long) nShorts - 1) * Short.SIZE + dstPos >= Long.SIZE) { + throw new IllegalArgumentException("(nShorts - 1) * 16 + dstPos >= 64"); } - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; + long out = dstInit; + for (int i = 0; i < nShorts; i++) { + final int shift = i * Short.SIZE + dstPos; + final long bits = (0xffffL & src[i + srcPos]) << shift; + final long mask = 0xffffL << shift; + out = out & ~mask | bits; } - return dst; + return out; } /** - *

    - * Converts an int into an array of boolean using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts a short into an array of boolean using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller or equal to - * the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 32} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length} + * @param src The short to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBools The number of booleans to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code nBools - 1 + srcPos >= 16}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length}. */ - public static boolean[] intToBinary(final int src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { + public static boolean[] shortToBinary(final short src, final int srcPos, final boolean[] dst, final int dstPos, final int nBools) { if (0 == nBools) { return dst; } - if (nBools - 1 + srcPos >= 32) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 32"); + if ((long) nBools - 1 + srcPos >= Short.SIZE) { + throw new IllegalArgumentException("nBools - 1 + srcPos >= 16"); } + assert nBools - 1 < Short.SIZE - srcPos; for (int i = 0; i < nBools; i++) { final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; + dst[dstPos + i] = (0x1 & src >> shift) != 0; } return dst; } /** - *

    - * Converts a short into an array of boolean using the default (little endian, Lsb0) byte - * and bit ordering. - *

    + * Converts a short into an array of byte using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the short to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller or equal to - * the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 16} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length} + * @param src The short to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBytes The number of bytes to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code (nBytes - 1) * 8 + srcPos >= 16}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length}. */ - public static boolean[] shortToBinary(final short src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { + public static byte[] shortToByteArray(final short src, final int srcPos, final byte[] dst, final int dstPos, final int nBytes) { + if (0 == nBytes) { return dst; } - if (nBools - 1 + srcPos >= 16) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 16"); + if (((long) nBytes - 1) * Byte.SIZE + srcPos >= Short.SIZE) { + throw new IllegalArgumentException("(nBytes - 1) * 8 + srcPos >= 16"); } - assert (nBools - 1) < 16 - srcPos; - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; + for (int i = 0; i < nBytes; i++) { + final int shift = i * Byte.SIZE + srcPos; + dst[dstPos + i] = (byte) (0xff & src >> shift); } return dst; } /** - *

    - * Converts a byte into an array of boolean using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Converts a short into an array of char using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the byte to convert - * @param srcPos the position in {@code src}, in bits, from where to start the conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller or equal to - * the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 8} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBools > dst.length} + * @param src The short to convert. + * @param srcPos The position in {@code src}, in bits, from where to start the conversion. + * @param dstInit The initial value for the result String. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nHexs The number of chars to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws IllegalArgumentException if {@code (nHexs - 1) * 4 + srcPos >= 16}. + * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos}. */ - public static boolean[] byteToBinary(final byte src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { - return dst; + public static String shortToHex(final short src, final int srcPos, final String dstInit, final int dstPos, final int nHexs) { + if (0 == nHexs) { + return dstInit; } - if (nBools - 1 + srcPos >= 8) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 8"); + if (((long) nHexs - 1) * 4 + srcPos >= Short.SIZE) { + throw new IllegalArgumentException("(nHexs - 1) * 4 + srcPos >= 16"); } - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; + final StringBuilder sb = new StringBuilder(dstInit); + int append = sb.length(); + for (int i = 0; i < nHexs; i++) { + final int shift = i * 4 + srcPos; + final int bits = 0xF & src >> shift; + if (dstPos + i == append) { + ++append; + sb.append(intToHexDigit(bits)); + } else { + sb.setCharAt(dstPos + i, intToHexDigit(bits)); + } } - return dst; + return sb.toString(); } /** - *

    - * Converts UUID into an array of byte using the default (little endian, Lsb0) byte and bit - * ordering. - *

    + * Converts UUID into an array of byte using the default (little-endian, LSB0) byte and bit ordering. * - * @param src the UUID to convert - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or equal to the - * width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBytes > 16} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length} + * @param src The UUID to convert. + * @param dst The destination array. + * @param dstPos The position in {@code dst} where to copy the result. + * @param nBytes The number of bytes to copy to {@code dst}, must be smaller or equal to the width of the input (from srcPos to MSB). + * @return {@code dst}. + * @throws NullPointerException if {@code dst} is {@code null}. + * @throws IllegalArgumentException if {@code nBytes > 16}. + * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nBytes > dst.length}. */ public static byte[] uuidToByteArray(final UUID src, final byte[] dst, final int dstPos, final int nBytes) { if (0 == nBytes) { return dst; } if (nBytes > 16) { - throw new IllegalArgumentException("nBytes is greater than 16"); + throw new IllegalArgumentException("nBytes > 16"); } - longToByteArray(src.getMostSignificantBits(), 0, dst, dstPos, nBytes > 8 ? 8 : nBytes); + longToByteArray(src.getMostSignificantBits(), 0, dst, dstPos, Math.min(nBytes, 8)); if (nBytes >= 8) { longToByteArray(src.getLeastSignificantBits(), 0, dst, dstPos + 8, nBytes - 8); } @@ -1542,22 +1389,12 @@ public static byte[] uuidToByteArray(final UUID src, final byte[] dst, final int } /** - *

    - * Converts bytes from an array into a UUID using the default (little endian, Lsb0) byte and - * bit ordering. - *

    + * Constructs a new instance. * - * @param src the byte array to convert - * @param srcPos the position in {@code src} where to copy the result from - * @return a UUID - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if array does not contain at least 16 bytes beginning - * with {@code srcPos} + * @deprecated Will be removed in 4.0.0. */ - public static UUID byteArrayToUuid(final byte[] src, final int srcPos) { - if (src.length - srcPos < 16) { - throw new IllegalArgumentException("Need at least 16 bytes for UUID"); - } - return new UUID(byteArrayToLong(src, srcPos, 0, 0, 8), byteArrayToLong(src, srcPos + 8, 0, 0, 8)); + @Deprecated + public Conversion() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/DoubleRange.java b/src/main/java/org/apache/commons/lang3/DoubleRange.java new file mode 100644 index 00000000000..da1185835b3 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/DoubleRange.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +/** + * Specializes {@link NumberRange} for {@link Double}s. + * + *

    + * This class is not designed to interoperate with other NumberRanges + *

    + * + * @since 3.13.0 + */ +public final class DoubleRange extends NumberRange { + + private static final long serialVersionUID = 1L; + + /** + * Creates a range with the specified minimum and maximum values (both inclusive). + * + *

    + * The range uses the natural ordering of the elements to determine where values lie in the range. + *

    + * + *

    + * The arguments may be passed in the order (min, max) or (max,min). The getMinimum and getMaximum methods will return the correct values. + *

    + * + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + * @throws IllegalArgumentException if either value is NaN. + */ + public static DoubleRange of(final double fromInclusive, final double toInclusive) { + return of(Double.valueOf(fromInclusive), Double.valueOf(toInclusive)); + } + + /** + * Creates a range with the specified minimum and maximum values (both inclusive). + * + *

    + * The range uses the natural ordering of the elements to determine where values lie in the range. + *

    + * + *

    + * The arguments may be passed in the order (min, max) or (max,min). The getMinimum and getMaximum methods will return the correct values. + *

    + * + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + * @throws NullPointerException if either element is null. + * @throws IllegalArgumentException if either element is NaN. + */ + public static DoubleRange of(final Double fromInclusive, final Double toInclusive) { + return new DoubleRange(fromInclusive, toInclusive); + } + + /** + * Creates an instance. + * + * @param number1 The first element, not null. + * @param number2 The second element, not null. + * @throws NullPointerException when element1 is null. + * @throws NullPointerException when element2 is null. + * @throws IllegalArgumentException when element1 or element2 is NaN. + */ + private DoubleRange(final Double number1, final Double number2) { + super(number1, number2, null); + } + + /** + * Fits the given value into this range by returning the given value or, if out of bounds, the range minimum if + * below, or the range maximum if above. + * + *
    {@code
    +     * LongRange range = LongRange.of(16, 64);
    +     * range.fit(-9) -->  16
    +     * range.fit(0)  -->  16
    +     * range.fit(15) -->  16
    +     * range.fit(16) -->  16
    +     * range.fit(17) -->  17
    +     * ...
    +     * range.fit(63) -->  63
    +     * range.fit(64) -->  64
    +     * range.fit(99) -->  64
    +     * }
    + * + * @param element The element to test. + * @return The minimum, the element, or the maximum depending on the element's location relative to the range. + * @since 3.19.0 + */ + public double fit(final double element) { + return super.fit(element).doubleValue(); + } + +} diff --git a/src/main/java/org/apache/commons/lang3/EnumUtils.java b/src/main/java/org/apache/commons/lang3/EnumUtils.java index 75b052a9fcf..a0557f18ecc 100644 --- a/src/main/java/org/apache/commons/lang3/EnumUtils.java +++ b/src/main/java/org/apache/commons/lang3/EnumUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -20,12 +20,18 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.stream.Streams; /** - *

    Utility library to provide helper methods for Java enums.

    + * Provides methods for Java enums. * *

    #ThreadSafe#

    * @@ -33,57 +39,58 @@ */ public class EnumUtils { - private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits"; - private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined."; + private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; + private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; /** * Validate {@code enumClass}. - * @param the type of the enumeration - * @param enumClass to check - * @return {@code enumClass} - * @throws NullPointerException if {@code enumClass} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class + * + * @param The type of the enumeration. + * @param enumClass to check. + * @return {@code enumClass}. + * @throws NullPointerException if {@code enumClass} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class. * @since 3.2 */ private static > Class asEnum(final Class enumClass) { - Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED); + Objects.requireNonNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED); Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass); return enumClass; } /** * Validate that {@code enumClass} is compatible with representation in a {@code long}. - * @param the type of the enumeration - * @param enumClass to check - * @return {@code enumClass} - * @throws NullPointerException if {@code enumClass} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * + * @param The type of the enumeration. + * @param enumClass to check. + * @return {@code enumClass}. + * @throws NullPointerException if {@code enumClass} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values. * @since 3.0.1 */ private static > Class checkBitVectorable(final Class enumClass) { final E[] constants = asEnum(enumClass).getEnumConstants(); - Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, - Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE)); - + Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, Integer.valueOf(constants.length), enumClass.getSimpleName(), + Integer.valueOf(Long.SIZE)); return enumClass; } /** - *

    Creates a long bit vector representation of the given array of Enum values.

    + * Creates a long bit vector representation of the given array of Enum values. * *

    This generates a value that is usable by {@link EnumUtils#processBitVector}.

    * *

    Do not use this method if you have more than 64 values in your Enum, as this * would create a value greater than a long can hold.

    * - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the values we want to convert, not {@code null} - * @param the type of the enumeration - * @return a long whose value provides a binary representation of the given set of enum values. - * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @param enumClass The class of the enum we are working with, not {@code null}. + * @param values The values we want to convert, not {@code null}. + * @param the type of the enumeration. + * @return A long whose value provides a binary representation of the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values. * @since 3.0.1 * @see #generateBitVectors(Class, Iterable) */ @@ -94,48 +101,48 @@ public static > long generateBitVector(final Class enumClas } /** - *

    Creates a long bit vector representation of the given subset of an Enum.

    + * Creates a long bit vector representation of the given subset of an Enum. * *

    This generates a value that is usable by {@link EnumUtils#processBitVector}.

    * *

    Do not use this method if you have more than 64 values in your Enum, as this * would create a value greater than a long can hold.

    * - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the values we want to convert, not {@code null}, neither containing {@code null} - * @param the type of the enumeration - * @return a long whose value provides a binary representation of the given set of enum values. - * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @param enumClass The class of the enum we are working with, not {@code null}. + * @param values The values we want to convert, not {@code null}, neither containing {@code null}. + * @param the type of the enumeration. + * @return A long whose value provides a binary representation of the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}. * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values, - * or if any {@code values} {@code null} + * or if any {@code values} {@code null}. * @since 3.0.1 * @see #generateBitVectors(Class, Iterable) */ public static > long generateBitVector(final Class enumClass, final Iterable values) { checkBitVectorable(enumClass); - Validate.notNull(values); + Objects.requireNonNull(values, "values"); long total = 0; for (final E constant : values) { - Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED); + Objects.requireNonNull(constant, NULL_ELEMENTS_NOT_PERMITTED); total |= 1L << constant.ordinal(); } return total; } /** - *

    Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.

    + * Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed. * *

    This generates a value that is usable by {@link EnumUtils#processBitVectors}.

    * *

    Use this method if you have more than 64 values in your Enum.

    * - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the values we want to convert, not {@code null}, neither containing {@code null} - * @param the type of the enumeration - * @return a long[] whose values provide a binary representation of the given set of enum values - * with least significant digits rightmost. - * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null} + * @param enumClass The class of the enum we are working with, not {@code null}. + * @param values The values we want to convert, not {@code null}, neither containing {@code null}. + * @param the type of the enumeration. + * @return A long[] whose values provide a binary representation of the given set of enum values + * with the least significant digits rightmost. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}. * @since 3.2 */ @SafeVarargs @@ -146,93 +153,90 @@ public static > long[] generateBitVectors(final Class enumC Collections.addAll(condensed, values); final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; for (final E value : condensed) { - result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE); + result[value.ordinal() / Long.SIZE] |= 1L << value.ordinal() % Long.SIZE; } ArrayUtils.reverse(result); return result; } /** - *

    Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.

    + * Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed. * *

    This generates a value that is usable by {@link EnumUtils#processBitVectors}.

    * *

    Use this method if you have more than 64 values in your Enum.

    * - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the values we want to convert, not {@code null}, neither containing {@code null} - * @param the type of the enumeration - * @return a long[] whose values provide a binary representation of the given set of enum values - * with least significant digits rightmost. - * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null} + * @param enumClass The class of the enum we are working with, not {@code null}. + * @param values The values we want to convert, not {@code null}, neither containing {@code null}. + * @param the type of the enumeration. + * @return A long[] whose values provide a binary representation of the given set of enum values + * with the least significant digits rightmost. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}. * @since 3.2 */ public static > long[] generateBitVectors(final Class enumClass, final Iterable values) { asEnum(enumClass); - Validate.notNull(values); + Objects.requireNonNull(values, "values"); final EnumSet condensed = EnumSet.noneOf(enumClass); - for (final E constant : values) { - Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED); - condensed.add(constant); - } + values.forEach(constant -> condensed.add(Objects.requireNonNull(constant, NULL_ELEMENTS_NOT_PERMITTED))); final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; for (final E value : condensed) { - result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE); + result[value.ordinal() / Long.SIZE] |= 1L << value.ordinal() % Long.SIZE; } ArrayUtils.reverse(result); return result; } /** - *

    Gets the enum for the class, returning {@code null} if not found.

    + * Gets the enum for the class, returning {@code null} if not found. * *

    This method differs from {@link Enum#valueOf} in that it does not throw an exception * for an invalid enum name.

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns null - * @return the enum, null if not found + * @param The type of the enumeration. + * @param enumClass The class of the enum to query, not null. + * @param enumName The enum name, null returns null. + * @return The enum, null if not found. */ public static > E getEnum(final Class enumClass, final String enumName) { return getEnum(enumClass, enumName, null); } /** - *

    Gets the enum for the class, returning {@code defaultEnum} if not found.

    + * Gets the enum for the class, returning {@code defaultEnum} if not found. * *

    This method differs from {@link Enum#valueOf} in that it does not throw an exception * for an invalid enum name.

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns default enum - * @param defaultEnum the default enum - * @return the enum, default enum if not found + * @param The type of the enumeration. + * @param enumClass The class of the enum to query, null returns default enum. + * @param enumName The enum name, null returns default enum. + * @param defaultEnum The default enum. + * @return The enum, default enum if not found. * @since 3.10 */ public static > E getEnum(final Class enumClass, final String enumName, final E defaultEnum) { - if (enumName == null) { + if (enumClass == null || enumName == null) { return defaultEnum; } try { return Enum.valueOf(enumClass, enumName); - } catch (final IllegalArgumentException ex) { + } catch (final IllegalArgumentException e) { return defaultEnum; } } /** - *

    Gets the enum for the class, returning {@code null} if not found.

    + * Gets the enum for the class, returning {@code null} if not found. * *

    This method differs from {@link Enum#valueOf} in that it does not throw an exception * for an invalid enum name and performs case insensitive matching of the name.

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns null - * @return the enum, null if not found + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, may be null. + * @param enumName The enum name, null returns null. + * @return The enum, null if not found. * @since 3.8 */ public static > E getEnumIgnoreCase(final Class enumClass, final String enumName) { @@ -240,86 +244,165 @@ public static > E getEnumIgnoreCase(final Class enumClass, } /** - *

    Gets the enum for the class, returning {@code defaultEnum} if not found.

    + * Gets the enum for the class, returning {@code defaultEnum} if not found. * *

    This method differs from {@link Enum#valueOf} in that it does not throw an exception * for an invalid enum name and performs case insensitive matching of the name.

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns default enum - * @param defaultEnum the default enum - * @return the enum, default enum if not found + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, null returns default enum. + * @param enumName The enum name, null returns default enum. + * @param defaultEnum The default enum. + * @return The enum, default enum if not found. * @since 3.10 */ - public static > E getEnumIgnoreCase(final Class enumClass, final String enumName, final E defaultEnum) { - if (enumName == null || !enumClass.isEnum()) { - return defaultEnum; - } - for (final E each : enumClass.getEnumConstants()) { - if (each.name().equalsIgnoreCase(enumName)) { - return each; - } - } - return defaultEnum; + public static > E getEnumIgnoreCase(final Class enumClass, final String enumName, + final E defaultEnum) { + return getFirstEnumIgnoreCase(enumClass, enumName, Enum::name, defaultEnum); } /** - *

    Gets the {@code List} of enums.

    + * Gets the {@link List} of enums. * *

    This method is useful when you need a list of enums rather than an array.

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @return the modifiable list of enums, never null + * @param The type of the enumeration. + * @param enumClass The class of the enum to query, not null. + * @return The modifiable list of enums, never null. */ public static > List getEnumList(final Class enumClass) { return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants())); } /** - *

    Gets the {@code Map} of enums by name.

    + * Gets the {@link Map} of enums by name. * *

    This method is useful when you need a map of enums by name.

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @return the modifiable map of enum names to enums, never null + * @param The type of the enumeration. + * @param enumClass The class of the enum to query, not null. + * @return The modifiable map of enum names to enums, never null. */ public static > Map getEnumMap(final Class enumClass) { - final Map map = new LinkedHashMap<>(); - for (final E e: enumClass.getEnumConstants()) { - map.put(e.name(), e); + return getEnumMap(enumClass, E::name); + } + + /** + * Gets the {@link Map} of enums by name. + * + *

    + * This method is useful when you need a map of enums by name. + *

    + * + * @param the type of enumeration. + * @param the type of the map key. + * @param enumClass The class of the enum to query, not null. + * @param keyFunction The function to query for the key, not null. + * @return The modifiable map of enums, never null. + * @since 3.13.0 + */ + public static , K> Map getEnumMap(final Class enumClass, final Function keyFunction) { + return stream(enumClass).collect(Collectors.toMap(keyFunction::apply, Function.identity())); + } + + /** + * Gets the enum for the class in a system property, returning {@code defaultEnum} if not found. + * + *

    + * This method differs from {@link Enum#valueOf} in that it does not throw an exception for an invalid enum name. + *

    + *

    + * If a {@link SecurityException} is caught, the return value is {@code null}. + *

    + * + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, not null. + * @param propName The system property key for the enum name, null returns default enum. + * @param defaultEnum The default enum. + * @return The enum, default enum if not found. + * @since 3.13.0 + */ + public static > E getEnumSystemProperty(final Class enumClass, final String propName, final E defaultEnum) { + return getEnum(enumClass, SystemProperties.getProperty(propName), defaultEnum); + } + + /** + * Gets the enum for the class and value, returning {@code defaultEnum} if not found. + * + *

    + * This method differs from {@link Enum#valueOf} in that it does not throw an exception for an invalid enum name and performs case insensitive matching of + * the name. + *

    + * + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, not null. + * @param value The enum name, null returns default enum. + * @param toIntFunction The function that gets an int for an enum for comparison to {@code value}. + * @param defaultEnum The default enum. + * @return An enum, default enum if not found. + * @since 3.18.0 + */ + public static > E getFirstEnum(final Class enumClass, final int value, final ToIntFunction toIntFunction, final E defaultEnum) { + if (!isEnum(enumClass)) { + return defaultEnum; + } + return stream(enumClass).filter(e -> value == toIntFunction.applyAsInt(e)).findFirst().orElse(defaultEnum); + } + + /** + * Gets the enum for the class, returning {@code defaultEnum} if not found. + * + *

    This method differs from {@link Enum#valueOf} in that it does not throw an exception + * for an invalid enum name and performs case insensitive matching of the name.

    + * + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, null returns default enum. + * @param enumName The enum name, null returns default enum. + * @param stringFunction The function that gets the string for an enum for comparison to {@code enumName}. + * @param defaultEnum The default enum. + * @return An enum, default enum if not found. + * @since 3.13.0 + */ + public static > E getFirstEnumIgnoreCase(final Class enumClass, final String enumName, final Function stringFunction, + final E defaultEnum) { + if (enumName == null) { + return defaultEnum; } - return map; + return stream(enumClass).filter(e -> enumName.equalsIgnoreCase(stringFunction.apply(e))).findFirst().orElse(defaultEnum); + } + + private static > boolean isEnum(final Class enumClass) { + return enumClass != null && enumClass.isEnum(); } /** - *

    Checks if the specified name is a valid enum for the class.

    + * Checks if the specified name is a valid enum for the class. * - *

    This method differs from {@link Enum#valueOf} in that checks if the name is - * a valid enum without needing to catch the exception.

    + *

    + * This method differs from {@link Enum#valueOf} in that it checks if the name is a valid enum without needing to catch the exception. + *

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns false - * @return true if the enum name is valid, otherwise false + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, null returns false. + * @param enumName The enum name, null returns false. + * @return true if the enum name is valid, otherwise false. */ public static > boolean isValidEnum(final Class enumClass, final String enumName) { return getEnum(enumClass, enumName) != null; } /** - *

    Checks if the specified name is a valid enum for the class.

    + * Checks if the specified name is a valid enum for the class. * - *

    This method differs from {@link Enum#valueOf} in that checks if the name is - * a valid enum without needing to catch the exception - * and performs case insensitive matching of the name.

    + *

    + * This method differs from {@link Enum#valueOf} in that it checks if the name is a valid enum without needing to catch the exception and performs case + * insensitive matching of the name. + *

    * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns false - * @return true if the enum name is valid, otherwise false + * @param the type of the enumeration. + * @param enumClass The class of the enum to query, null returns false. + * @param enumName The enum name, null returns false. + * @return true if the enum name is valid, otherwise false. * @since 3.8 */ public static > boolean isValidEnumIgnoreCase(final Class enumClass, final String enumName) { @@ -327,53 +410,71 @@ public static > boolean isValidEnumIgnoreCase(final Class e } /** - *

    Convert a long value created by {@link EnumUtils#generateBitVector} into the set of - * enum values that it represents.

    + * Convert a long value created by {@link EnumUtils#generateBitVector} into the set of + * enum values that it represents. * *

    If you store this value, beware any changes to the enum that would affect ordinal values.

    - * @param enumClass the class of the enum we are working with, not {@code null} - * @param value the long value representation of a set of enum values - * @param the type of the enumeration - * @return a set of enum values - * @throws NullPointerException if {@code enumClass} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * + * @param enumClass The class of the enum we are working with, not {@code null}. + * @param value The long value representation of a set of enum values. + * @param the type of the enumeration. + * @return A set of enum values. + * @throws NullPointerException if {@code enumClass} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values. * @since 3.0.1 */ public static > EnumSet processBitVector(final Class enumClass, final long value) { - checkBitVectorable(enumClass).getEnumConstants(); - return processBitVectors(enumClass, value); + return processBitVectors(checkBitVectorable(enumClass), value); } /** - *

    Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of - * enum values that it represents.

    + * Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of + * enum values that it represents. * *

    If you store this value, beware any changes to the enum that would affect ordinal values.

    - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null} - * @param the type of the enumeration - * @return a set of enum values - * @throws NullPointerException if {@code enumClass} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class + * + * @param enumClass The class of the enum we are working with, not {@code null}. + * @param values The long[] bearing the representation of a set of enum values, the least significant digits rightmost, not {@code null}. + * @param the type of the enumeration. + * @return A set of enum values. + * @throws NullPointerException if {@code enumClass} is {@code null}. + * @throws IllegalArgumentException if {@code enumClass} is not an enum class. * @since 3.2 */ public static > EnumSet processBitVectors(final Class enumClass, final long... values) { final EnumSet results = EnumSet.noneOf(asEnum(enumClass)); - final long[] lvalues = ArrayUtils.clone(Validate.notNull(values)); + final long[] lvalues = ArrayUtils.clone(Objects.requireNonNull(values, "values")); ArrayUtils.reverse(lvalues); - for (final E constant : enumClass.getEnumConstants()) { + stream(enumClass).forEach(constant -> { final int block = constant.ordinal() / Long.SIZE; - if (block < lvalues.length && (lvalues[block] & 1L << (constant.ordinal() % Long.SIZE)) != 0) { + if (block < lvalues.length && (lvalues[block] & 1L << constant.ordinal() % Long.SIZE) != 0) { results.add(constant); } - } + }); return results; } + /** + * Returns a sequential ordered stream whose elements are the given class' enum values. + * + * @param the type of stream elements. + * @param clazz The class containing the enum values, may be null. + * @return The new stream, empty of {@code clazz} is null. + * @since 3.18.0 + * @see Class#getEnumConstants() + */ + public static Stream stream(final Class clazz) { + return clazz != null ? Streams.of(clazz.getEnumConstants()) : Stream.empty(); + } + /** * This constructor is public to permit tools that require a JavaBean * instance to operate. + * + * @deprecated TODO Make private in 4.0. */ + @Deprecated public EnumUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/Functions.java b/src/main/java/org/apache/commons/lang3/Functions.java index ef9c45d909b..12325bd4c98 100644 --- a/src/main/java/org/apache/commons/lang3/Functions.java +++ b/src/main/java/org/apache/commons/lang3/Functions.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; import java.util.Collection; import java.util.Objects; import java.util.concurrent.Callable; @@ -32,533 +33,634 @@ import java.util.stream.Stream; import org.apache.commons.lang3.Streams.FailableStream; - - -/** This class provides utility functions, and classes for working with the - * {@code java.util.function} package, or more generally, with Java 8 - * lambdas. - * More specifically, it attempts to address the fact that lambdas are supposed - * not to throw Exceptions, at least not checked Exceptions, aka instances of - * {@link Exception}. This enforces the use of constructs like +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.function.Failable; +import org.apache.commons.lang3.function.FailableBooleanSupplier; + +/** + * This class provides utility functions, and classes for working with the {@code java.util.function} package, or more + * generally, with Java 8 lambdas. More specifically, it attempts to address the fact that lambdas are supposed not to + * throw Exceptions, at least not checked Exceptions, AKA instances of {@link Exception}. This enforces the use of + * constructs like: + * *
    - *   Consumer<java.lang.reflect.Method> consumer = (m) -> {
    - *       try {
    - *           m.invoke(o, args);
    - *       } catch (Throwable t) {
    - *           throw Functions.rethrow(t);
    - *       }
    - *   };
    - * 
    - * By replacing a {@link java.util.function.Consumer Consumer<O>} with a - * {@link FailableConsumer FailableConsumer<O,? extends Throwable>}, this can be - * written like follows: + * {@code + * Consumer consumer = m -> { + * try { + * m.invoke(o, args); + * } catch (Throwable t) { + * throw Functions.rethrow(t); + * } + * }; + * } + * + *

    + * By replacing a {@link java.util.function.Consumer Consumer<O>} with a {@link FailableConsumer + * FailableConsumer<O,? extends Throwable>}, this can be written like follows: + *

    + * *
    - *   Functions.accept((m) -> m.invoke(o,args));
    - * 
    - * Obviously, the second version is much more concise and the spirit of - * Lambda expressions is met better than the second version. + * {@code + * Functions.accept((m) -> m.invoke(o,args)); + * } + * + *

    + * Obviously, the second version is much more concise and the spirit of Lambda expressions is met better than the second + * version. + *

    + * + * @since 3.9 + * @deprecated Use {@link org.apache.commons.lang3.function.Failable}. */ +@Deprecated public class Functions { + /** + * A functional interface like {@link BiConsumer} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Consumed type 1. + * @param Consumed type 2. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableBiConsumer}. + */ + @Deprecated @FunctionalInterface - public interface FailableRunnable { + public interface FailableBiConsumer { + /** - * Runs the function. - * @throws T if the function fails + * Accepts the consumer. + * + * @param object1 The first parameter for the consumable to accept + * @param object2 The second parameter for the consumable to accept + * @throws T Thrown when the consumer fails. */ - void run() throws T; + void accept(O1 object1, O2 object2) throws T; } + /** + * A functional interface like {@link BiFunction} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Input type 1. + * @param Input type 2. + * @param Return type. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableBiFunction}. + */ + @Deprecated @FunctionalInterface - public interface FailableCallable { + public interface FailableBiFunction { + /** - * Calls the callable. - * @return The value returned from the callable - * @throws T if the callable fails + * Applies this function. + * + * @param input1 The first input for the function + * @param input2 The second input for the function + * @return The result of the function + * @throws T Thrown when the function fails. */ - O call() throws T; + R apply(O1 input1, O2 input2) throws T; } + /** + * A functional interface like {@link BiPredicate} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Predicate type 1. + * @param Predicate type 2. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableBiPredicate}. + */ + @Deprecated @FunctionalInterface - public interface FailableConsumer { + public interface FailableBiPredicate { + /** - * Accepts the consumer. - * @param pObject the parameter for the consumable to accept - * @throws T if the consumer fails + * Tests the predicate. + * + * @param object1 The first object to test the predicate on + * @param object2 The second object to test the predicate on + * @return The predicate's evaluation + * @throws T if the predicate fails */ - void accept(O pObject) throws T; + boolean test(O1 object1, O2 object2) throws T; } + /** + * A functional interface like {@link java.util.concurrent.Callable} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Return type. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableCallable}. + */ + @Deprecated @FunctionalInterface - public interface FailableBiConsumer { + public interface FailableCallable { + /** - * Accepts the consumer. - * @param pObject1 the first parameter for the consumable to accept - * @param pObject2 the second parameter for the consumable to accept - * @throws T if the consumer fails + * Calls the callable. + * + * @return The value returned from the callable + * @throws T if the callable fails */ - void accept(O1 pObject1, O2 pObject2) throws T; + R call() throws T; } + /** + * A functional interface like {@link Consumer} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Consumed type 1. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableConsumer}. + */ + @Deprecated @FunctionalInterface - public interface FailableFunction { + public interface FailableConsumer { + /** - * Apply the function. - * @param pInput the input for the function - * @return the result of the function - * @throws T if the function fails + * Accepts the consumer. + * + * @param object The parameter for the consumable to accept + * @throws T Thrown when the consumer fails. */ - O apply(I pInput) throws T; + void accept(O object) throws T; } + /** + * A functional interface like {@link Function} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Input type 1. + * @param Return type. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableFunction}. + */ + @Deprecated @FunctionalInterface - public interface FailableBiFunction { + public interface FailableFunction { + /** - * Apply the function. - * @param pInput1 the first input for the function - * @param pInput2 the second input for the function - * @return the result of the function - * @throws T if the function fails + * Applies this function. + * + * @param input The input for the function + * @return The result of the function + * @throws T Thrown when the function fails. */ - O apply(I1 pInput1, I2 pInput2) throws T; + R apply(I input) throws T; } + /** + * A functional interface like {@link Predicate} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Predicate type 1. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailablePredicate}. + */ + @Deprecated @FunctionalInterface - public interface FailablePredicate { + public interface FailablePredicate { + /** - * Test the predicate. - * @param pObject the object to test the predicate on - * @return the predicate's evaluation + * Tests the predicate. + * + * @param object The object to test the predicate on + * @return The predicate's evaluation * @throws T if the predicate fails */ - boolean test(O pObject) throws T; + boolean test(I object) throws T; } + /** + * A functional interface like {@link Runnable} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableRunnable}. + */ + @Deprecated @FunctionalInterface - public interface FailableBiPredicate { + public interface FailableRunnable { + /** - * Test the predicate. - * @param pObject1 the first object to test the predicate on - * @param pObject2 the second object to test the predicate on - * @return the predicate's evaluation - * @throws T if the predicate fails + * Runs the function. + * + * @throws T Thrown when the function fails. */ - boolean test(O1 pObject1, O2 pObject2) throws T; + void run() throws T; } + /** + * A functional interface like {@link Supplier} that declares a {@link Throwable}. + * + *

    TODO for 4.0: Move to org.apache.commons.lang3.function.

    + * + * @param Return type. + * @param Thrown exception. + * @deprecated Use {@link org.apache.commons.lang3.function.FailableSupplier}. + */ + @Deprecated @FunctionalInterface - public interface FailableSupplier { + public interface FailableSupplier { + /** * Supplies an object - * @return the suppliers result + * + * @return A result * @throws T if the supplier fails */ - O get() throws T; + R get() throws T; } /** - * Converts the given {@link FailableRunnable} into a standard {@link Runnable}. + * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. * - * @param pRunnable a {@code FailableRunnable} - * @return a standard {@code Runnable} + * @param consumer The consumer to consume + * @param object1 The first object to consume by {@code consumer} + * @param object2 The second object to consume by {@code consumer} + * @param the type of the first argument the consumer accepts + * @param the type of the second argument the consumer accepts + * @param The type of checked exception the consumer may throw */ - public static Runnable asRunnable(FailableRunnable pRunnable) { - return () -> run(pRunnable); + public static void accept(final FailableBiConsumer consumer, + final O1 object1, final O2 object2) { + run(() -> consumer.accept(object1, object2)); } /** - * Converts the given {@link FailableConsumer} into a standard {@link Consumer}. + * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. * - * @param the type used by the consumers - * @param pConsumer a {@code FailableConsumer} - * @return a standard {@code Consumer} + * @param consumer The consumer to consume + * @param object The object to consume by {@code consumer} + * @param The type the consumer accepts + * @param The type of checked exception the consumer may throw */ - public static Consumer asConsumer(FailableConsumer pConsumer) { - return (pInput) -> accept(pConsumer, pInput); + public static void accept(final FailableConsumer consumer, final O object) { + run(() -> consumer.accept(object)); } /** - * Converts the given {@link FailableCallable} into a standard {@link Callable}. + * Applies a function and rethrows any exception as a {@link RuntimeException}. * - * @param the type used by the callables - * @param pCallable a {@code FailableCallable} - * @return a standard {@code Callable} + * @param function The function to apply + * @param input1 The first input to apply {@code function} on + * @param input2 The second input to apply {@code function} on + * @param the type of the first argument the function accepts + * @param the type of the second argument the function accepts + * @param The return type of the function + * @param The type of checked exception the function may throw + * @return The value returned from the function */ - public static Callable asCallable(FailableCallable pCallable) { - return () -> call(pCallable); + public static O apply(final FailableBiFunction function, + final O1 input1, final O2 input2) { + return get(() -> function.apply(input1, input2)); } /** - * Converts the given {@link FailableBiConsumer} into a standard {@link BiConsumer}. + * Applies a function and rethrows any exception as a {@link RuntimeException}. * - * @param the type of the first argument of the consumers - * @param the type of the second argument of the consumers - * @param pConsumer a failable {@code BiConsumer} - * @return a standard {@code BiConsumer} + * @param function The function to apply + * @param input The input to apply {@code function} on + * @param The type of the argument the function accepts + * @param The return type of the function + * @param The type of checked exception the function may throw + * @return The value returned from the function */ - public static BiConsumer asBiConsumer(FailableBiConsumer pConsumer) { - return (pInput1, pInput2) -> accept(pConsumer, pInput1, pInput2); + public static O apply(final FailableFunction function, final I input) { + return get(() -> function.apply(input)); } /** - * Converts the given {@link FailableFunction} into a standard {@link Function}. + * Converts the given {@link FailableBiConsumer} into a standard {@link BiConsumer}. * - * @param the type of the input of the functions - * @param the type of the output of the functions - * @param pFunction a {code FailableFunction} - * @return a standard {@code Function} + * @param the type of the first argument of the consumers + * @param the type of the second argument of the consumers + * @param consumer A failable {@link BiConsumer} + * @return A standard {@link BiConsumer} + * @since 3.10 */ - public static Function asFunction(FailableFunction pFunction) { - return (pInput) -> apply(pFunction, pInput); + public static BiConsumer asBiConsumer(final FailableBiConsumer consumer) { + return (input1, input2) -> accept(consumer, input1, input2); } /** * Converts the given {@link FailableBiFunction} into a standard {@link BiFunction}. * - * @param the type of the first argument of the input of the functions - * @param the type of the second argument of the input of the functions - * @param the type of the output of the functions - * @param pFunction a {@code FailableBiFunction} - * @return a standard {@code BiFunction} + * @param the type of the first argument of the input of the functions + * @param the type of the second argument of the input of the functions + * @param The type of the output of the functions + * @param function A {@link FailableBiFunction} + * @return A standard {@link BiFunction} + * @since 3.10 */ - public static BiFunction asBiFunction(FailableBiFunction pFunction) { - return (pInput1, pInput2) -> apply(pFunction, pInput1, pInput2); + public static BiFunction asBiFunction(final FailableBiFunction function) { + return (input1, input2) -> apply(function, input1, input2); } /** - * Converts the given {@link FailablePredicate} into a standard {@link Predicate}. + * Converts the given {@link FailableBiPredicate} into a standard {@link BiPredicate}. * - * @param the type used by the predicates - * @param pPredicate a {@code FailablePredicate} - * @return a standard {@code Predicate} + * @param the type of the first argument used by the predicates + * @param the type of the second argument used by the predicates + * @param predicate A {@link FailableBiPredicate} + * @return A standard {@link BiPredicate} + * @since 3.10 */ - public static Predicate asPredicate(FailablePredicate pPredicate) { - return (pInput) -> test(pPredicate, pInput); + public static BiPredicate asBiPredicate(final FailableBiPredicate predicate) { + return (input1, input2) -> test(predicate, input1, input2); } /** - * Converts the given {@link FailableBiPredicate} into a standard {@link BiPredicate}. + * Converts the given {@link FailableCallable} into a standard {@link Callable}. * - * @param the type of the first argument used by the predicates - * @param the type of the second argument used by the predicates - * @param pPredicate a {@code FailableBiPredicate} - * @return a standard {@code BiPredicate} + * @param The type used by the callables + * @param callable A {@link FailableCallable} + * @return A standard {@link Callable} + * @since 3.10 */ - public static BiPredicate asBiPredicate(FailableBiPredicate pPredicate) { - return (pInput1, pInput2) -> test(pPredicate, pInput1, pInput2); + public static Callable asCallable(final FailableCallable callable) { + return () -> call(callable); } /** - * Converts the given {@link FailableSupplier} into a standard {@link Supplier}. + * Converts the given {@link FailableConsumer} into a standard {@link Consumer}. * - * @param the type supplied by the suppliers - * @param pSupplier a {@code FailableSupplier} - * @return a standard {@code Supplier} + * @param The type used by the consumers + * @param consumer A {@link FailableConsumer} + * @return A standard {@link Consumer} + * @since 3.10 */ - public static Supplier asSupplier(FailableSupplier pSupplier) { - return () -> get(pSupplier); + public static Consumer asConsumer(final FailableConsumer consumer) { + return input -> accept(consumer, input); } /** - * Runs a runnable and rethrows any exception as a {@link RuntimeException}. - * @param pRunnable The runnable to run - * @param the type of checked exception the runnable may throw + * Converts the given {@link FailableFunction} into a standard {@link Function}. + * + * @param The type of the input of the functions + * @param The type of the output of the functions + * @param function A {code FailableFunction} + * @return A standard {@link Function} + * @since 3.10 */ - public static void run(FailableRunnable pRunnable) { - try { - pRunnable.run(); - } catch (Throwable t) { - throw rethrow(t); - } + public static Function asFunction(final FailableFunction function) { + return input -> apply(function, input); } /** - * Calls a callable and rethrows any exception as a {@link RuntimeException}. - * @param pCallable the callable to call - * @param the return type of the callable - * @param the type of checked exception the callable may throw - * @return the value returned from the callable + * Converts the given {@link FailablePredicate} into a standard {@link Predicate}. + * + * @param The type used by the predicates + * @param predicate A {@link FailablePredicate} + * @return A standard {@link Predicate} + * @since 3.10 */ - public static O call(FailableCallable pCallable) { - try { - return pCallable.call(); - } catch (Throwable t) { - throw rethrow(t); - } + public static Predicate asPredicate(final FailablePredicate predicate) { + return input -> test(predicate, input); } /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * @param pConsumer the consumer to consume - * @param pObject the object to consume by {@code pConsumer} - * @param the type the consumer accepts - * @param the type of checked exception the consumer may throw + * Converts the given {@link FailableRunnable} into a standard {@link Runnable}. + * + * @param runnable A {@link FailableRunnable} + * @return A standard {@link Runnable} + * @since 3.10 */ - public static void accept(FailableConsumer pConsumer, O pObject) { - try { - pConsumer.accept(pObject); - } catch (Throwable t) { - throw rethrow(t); - } + public static Runnable asRunnable(final FailableRunnable runnable) { + return () -> run(runnable); } /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * @param pConsumer the consumer to consume - * @param pObject1 the first object to consume by {@code pConsumer} - * @param pObject2 the second object to consume by {@code pConsumer} - * @param the type of the first argument the consumer accepts - * @param the type of the second argument the consumer accepts - * @param the type of checked exception the consumer may throw + * Converts the given {@link FailableSupplier} into a standard {@link Supplier}. + * + * @param The type supplied by the suppliers + * @param supplier A {@link FailableSupplier} + * @return A standard {@link Supplier} + * @since 3.10 */ - public static void accept(FailableBiConsumer pConsumer, O1 pObject1, O2 pObject2) { - try { - pConsumer.accept(pObject1, pObject2); - } catch (Throwable t) { - throw rethrow(t); - } + public static Supplier asSupplier(final FailableSupplier supplier) { + return () -> get(supplier); } /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * @param pFunction the function to apply - * @param pInput the input to apply {@code pFunction} on - * @param the type of the argument the function accepts - * @param the return type of the function - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static O apply(FailableFunction pFunction, I pInput) { - try { - return pFunction.apply(pInput); - } catch (Throwable t) { - throw rethrow(t); - } + * Calls a callable and rethrows any exception as a {@link RuntimeException}. + * + * @param callable The callable to call + * @param The return type of the callable + * @param The type of checked exception the callable may throw + * @return The value returned from the callable + */ + public static O call(final FailableCallable callable) { + return get(callable::call); } /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * @param pFunction the function to apply - * @param pInput1 the first input to apply {@code pFunction} on - * @param pInput2 the second input to apply {@code pFunction} on - * @param the type of the first argument the function accepts - * @param the type of the second argument the function accepts - * @param the return type of the function - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static O apply(FailableBiFunction pFunction, I1 pInput1, I2 pInput2) { + * Invokes a supplier, and returns the result. + * + * @param supplier The supplier to invoke. + * @param The suppliers output type. + * @param The type of checked exception, which the supplier can throw. + * @return The object, which has been created by the supplier + * @since 3.10 + */ + public static O get(final FailableSupplier supplier) { try { - return pFunction.apply(pInput1, pInput2); - } catch (Throwable t) { + return supplier.get(); + } catch (final Throwable t) { throw rethrow(t); } } /** - * Tests a predicate and rethrows any exception as a {@link RuntimeException}. - * @param pPredicate the predicate to test - * @param pObject the input to test by {@code pPredicate} - * @param the type of argument the predicate tests - * @param the type of checked exception the predicate may throw - * @return the boolean value returned by the predicate + * Invokes a boolean supplier, and returns the result. + * + * @param supplier The boolean supplier to invoke. + * @param The type of checked exception, which the supplier can throw. + * @return The boolean, which has been created by the supplier */ - public static boolean test(FailablePredicate pPredicate, O pObject) { + private static boolean getAsBoolean(final FailableBooleanSupplier supplier) { try { - return pPredicate.test(pObject); - } catch (Throwable t) { + return supplier.getAsBoolean(); + } catch (final Throwable t) { throw rethrow(t); } } /** - * Tests a predicate and rethrows any exception as a {@link RuntimeException}. - * @param pPredicate the predicate to test - * @param pObject1 the first input to test by {@code pPredicate} - * @param pObject2 the second input to test by {@code pPredicate} - * @param the type of the first argument the predicate tests - * @param the type of the second argument the predicate tests - * @param the type of checked exception the predicate may throw - * @return the boolean value returned by the predicate + * Rethrows a {@link Throwable} as an unchecked exception. If the argument is already unchecked, namely a + * {@link RuntimeException} or {@link Error} then the argument will be rethrown without modification. If the + * exception is {@link IOException} then it will be wrapped into a {@link UncheckedIOException}. In every other + * cases the exception will be wrapped into a {@code + * UndeclaredThrowableException} + * + *

    + * Note that there is a declared return type for this method, even though it never returns. The reason for that is + * to support the usual pattern: + *

    + * + *
    +     * throw rethrow(myUncheckedException);
    + * + *

    + * instead of just calling the method. This pattern may help the Java compiler to recognize that at that point an + * exception will be thrown and the code flow analysis will not demand otherwise mandatory commands that could + * follow the method call, like a {@code return} statement from a value returning method. + *

    + * + * @param throwable The throwable to rethrow possibly wrapped into an unchecked exception + * @return Never returns anything, this method never terminates normally. */ - public static boolean test(FailableBiPredicate pPredicate, O1 pObject1, O2 pObject2) { - try { - return pPredicate.test(pObject1, pObject2); - } catch (Throwable t) { - throw rethrow(t); + public static RuntimeException rethrow(final Throwable throwable) { + Objects.requireNonNull(throwable, "throwable"); + ExceptionUtils.throwUnchecked(throwable); + if (throwable instanceof IOException) { + throw new UncheckedIOException((IOException) throwable); } + throw new UndeclaredThrowableException(throwable); } /** - * Invokes the supplier, and returns the result. - * @param pSupplier The supplier to invoke. - * @param The suppliers output type. - * @param The type of checked exception, which the supplier can throw. - * @return The object, which has been created by the supplier + * Runs a runnable and rethrows any exception as a {@link RuntimeException}. + * + * @param runnable The runnable to run + * @param The type of checked exception the runnable may throw */ - public static O get(FailableSupplier pSupplier) { + public static void run(final FailableRunnable runnable) { try { - return pSupplier.get(); - } catch (Throwable t) { + runnable.run(); + } catch (final Throwable t) { throw rethrow(t); } } /** - * Converts the given stream into a {@link FailableStream}. The - * {@link FailableStream} consists of the same elements, than the - * input stream. However, failable lambdas, like - * {@link FailablePredicate}, {@link FailableFunction}, and - * {@link FailableConsumer} may be applied, rather than - * {@link Predicate}, {@link Function}, {@link Consumer}, etc. - * @param pStream The stream, which is being converted into a - * {@link FailableStream}. - * @param The streams element type. + * Converts the given collection into a {@link FailableStream}. The {@link FailableStream} consists of the + * collections elements. Shortcut for + * + *
    +     * Functions.stream(collection.stream());
    + * + * @param collection The collection, which is being converted into a {@link FailableStream}. + * @param The collections element type. (In turn, the result streams element type.) * @return The created {@link FailableStream}. + * @since 3.10 */ - public static FailableStream stream(Stream pStream) { - return new FailableStream(pStream); + public static FailableStream stream(final Collection collection) { + return new FailableStream<>(collection.stream()); } /** - * Converts the given collection into a {@link FailableStream}. - * The {@link FailableStream} consists of the collections - * elements. Shortcut for - *
    -     *   Functions.stream(pCollection.stream());
    -     * 
    - * @param pCollection The collection, which is being converted into a - * {@link FailableStream}. - * @param The collections element type. (In turn, the result - * streams element type.) + * Converts the given stream into a {@link FailableStream}. The {@link FailableStream} consists of the same + * elements, than the input stream. However, failable lambdas, like {@link FailablePredicate}, + * {@link FailableFunction}, and {@link FailableConsumer} may be applied, rather than {@link Predicate}, + * {@link Function}, {@link Consumer}, etc. + * + * @param stream The stream, which is being converted into a {@link FailableStream}. + * @param The streams element type. * @return The created {@link FailableStream}. + * @since 3.10 */ - public static FailableStream stream(Collection pCollection) { - return new FailableStream(pCollection.stream()); + public static FailableStream stream(final Stream stream) { + return new FailableStream<>(stream); } + /** + * Tests a predicate and rethrows any exception as a {@link RuntimeException}. + * + * @param predicate The predicate to test + * @param object1 The first input to test by {@code predicate} + * @param object2 The second input to test by {@code predicate} + * @param the type of the first argument the predicate tests + * @param the type of the second argument the predicate tests + * @param The type of checked exception the predicate may throw + * @return The boolean value returned by the predicate + */ + public static boolean test(final FailableBiPredicate predicate, + final O1 object1, final O2 object2) { + return getAsBoolean(() -> predicate.test(object1, object2)); + } /** - * A simple try-with-resources implementation, that can be used, if your - * objects do not implement the {@link AutoCloseable} interface. The method - * executes the {@code pAction}. The method guarantees, that all - * the {@code pResources} are being executed, in the given order, afterwards, - * and regardless of success, or failure. If either the original action, or - * any of the resource action fails, then the first failure (aka - * {@link Throwable} is rethrown. Example use: + * Tests a predicate and rethrows any exception as a {@link RuntimeException}. + * + * @param predicate The predicate to test + * @param object The input to test by {@code predicate} + * @param The type of argument the predicate tests + * @param The type of checked exception the predicate may throw + * @return The boolean value returned by the predicate + */ + public static boolean test(final FailablePredicate predicate, final O object) { + return getAsBoolean(() -> predicate.test(object)); + } + + /** + * A simple try-with-resources implementation, that can be used, if your objects do not implement the + * {@link AutoCloseable} interface. The method executes the {@code action}. The method guarantees, that all + * the {@code resources} are being executed, in the given order, afterwards, and regardless of success, or failure. + * If either the original action, or any of the resource action fails, then the first failure (AKA + * {@link Throwable}) is rethrown. Example use: + * *
    -     *   final FileInputStream fis = new FileInputStream("my.file");
    -     *   Functions.tryWithResources(useInputStream(fis), null, () -> fis.close());
    -     * 
    - * @param pAction The action to execute. This object will always - * be invoked. - * @param pErrorHandler An optional error handler, which will be invoked finally, - * if any error occurred. The error handler will receive the first - * error, aka {@link Throwable}. - * @param pResources The resource actions to execute. All resource - * actions will be invoked, in the given order. A resource action is an - * instance of {@link FailableRunnable}, which will be executed. + * {@code + * final FileInputStream fis = new FileInputStream("my.file"); + * Functions.tryWithResources(useInputStream(fis), null, () -> fis.close()); + * } + * + * @param action The action to execute. This object will always be invoked. + * @param errorHandler An optional error handler, which will be invoked finally, if any error occurred. The error + * handler will receive the first error, AKA {@link Throwable}. + * @param resources The resource actions to execute. All resource actions will be invoked, in the given + * order. A resource action is an instance of {@link FailableRunnable}, which will be executed. * @see #tryWithResources(FailableRunnable, FailableRunnable...) */ @SafeVarargs - public static void tryWithResources(FailableRunnable pAction, - FailableConsumer pErrorHandler, - FailableRunnable... pResources) { - final FailableConsumer errorHandler; - if (pErrorHandler == null) { - errorHandler = (t) -> rethrow(t); - } else { - errorHandler = pErrorHandler; - } - if (pResources != null) { - for (FailableRunnable failableRunnable : pResources) { - Objects.requireNonNull(failableRunnable, "runnable"); - } - } - Throwable th = null; - try { - pAction.run(); - } catch (Throwable t) { - th = t; - } - if (pResources != null) { - for (FailableRunnable runnable : pResources) { - try { - runnable.run(); - } catch (Throwable t) { - if (th == null) { - th = t; - } - } - } - } - if (th != null) { - try { - errorHandler.accept(th); - } catch (Throwable t) { - throw rethrow(t); - } - } + public static void tryWithResources(final FailableRunnable action, + final FailableConsumer errorHandler, + final FailableRunnable... resources) { + final org.apache.commons.lang3.function.FailableRunnable[] fr = new org.apache.commons.lang3.function.FailableRunnable[resources.length]; + Arrays.setAll(fr, i -> () -> resources[i].run()); + Failable.tryWithResources(action::run, errorHandler != null ? errorHandler::accept : null, fr); } /** - * A simple try-with-resources implementation, that can be used, if your - * objects do not implement the {@link AutoCloseable} interface. The method - * executes the {@code pAction}. The method guarantees, that all - * the {@code pResources} are being executed, in the given order, afterwards, - * and regardless of success, or failure. If either the original action, or - * any of the resource action fails, then the first failure (aka - * {@link Throwable} is rethrown. Example use: + * A simple try-with-resources implementation, that can be used, if your objects do not implement the + * {@link AutoCloseable} interface. The method executes the {@code action}. The method guarantees, that all + * the {@code resources} are being executed, in the given order, afterwards, and regardless of success, or failure. + * If either the original action, or any of the resource action fails, then the first failure (AKA + * {@link Throwable}) is rethrown. Example use: + * *
    -     *   final FileInputStream fis = new FileInputStream("my.file");
    -     *   Functions.tryWithResources(useInputStream(fis), () -> fis.close());
    -     * 
    - * @param pAction The action to execute. This object will always - * be invoked. - * @param pResources The resource actions to execute. All resource - * actions will be invoked, in the given order. A resource action is an - * instance of {@link FailableRunnable}, which will be executed. + * {@code + * final FileInputStream fis = new FileInputStream("my.file"); + * Functions.tryWithResources(useInputStream(fis), () -> fis.close()); + * } + * + * @param action The action to execute. This object will always be invoked. + * @param resources The resource actions to execute. All resource actions will be invoked, in the given + * order. A resource action is an instance of {@link FailableRunnable}, which will be executed. * @see #tryWithResources(FailableRunnable, FailableConsumer, FailableRunnable...) */ @SafeVarargs - public static void tryWithResources(FailableRunnable pAction, - FailableRunnable... pResources) { - tryWithResources(pAction, null, pResources); + public static void tryWithResources(final FailableRunnable action, + final FailableRunnable... resources) { + tryWithResources(action, null, resources); } /** - *

    Rethrows a {@link Throwable} as an unchecked exception. If the argument is - * already unchecked, namely a {@code RuntimeException} or {@code Error} then - * the argument will be rethrown without modification. If the exception is - * {@code IOException} then it will be wrapped into a {@code UncheckedIOException}. - * In every other cases the exception will be wrapped into a {@code - * UndeclaredThrowableException}

    - * - *

    Note that there is a declared return type for this method, even though it - * never returns. The reason for that is to support the usual pattern:

    - * - *
    -     *      throw rethrow(myUncheckedException);
    -     * 
    - * - *

    instead of just calling the method. This pattern may help the Java compiler to - * recognize that at that point an exception will be thrown and the code flow - * analysis will not demand otherwise mandatory commands that could follow the - * method call, like a {@code return} statement from a value returning method.

    - * - * @param pThrowable The throwable to rethrow possibly wrapped into an unchecked exception - * @return Never returns anything, this method never terminates normally. + * Constructs a new instance. */ - public static RuntimeException rethrow(Throwable pThrowable) { - Objects.requireNonNull(pThrowable, "pThrowable"); - if (pThrowable instanceof RuntimeException) { - throw (RuntimeException) pThrowable; - } else if (pThrowable instanceof Error) { - throw (Error) pThrowable; - } else if (pThrowable instanceof IOException) { - throw new UncheckedIOException((IOException) pThrowable); - } else { - throw new UndeclaredThrowableException(pThrowable); - } + public Functions() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/IntegerRange.java b/src/main/java/org/apache/commons/lang3/IntegerRange.java new file mode 100644 index 00000000000..4809e477f80 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/IntegerRange.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.util.stream.IntStream; + +/** + * Specializes {@link NumberRange} for {@link Integer}s. + * + *

    + * This class is not designed to interoperate with other NumberRanges + *

    + * + * @since 3.13.0 + */ +public final class IntegerRange extends NumberRange { + + private static final long serialVersionUID = 1L; + + /** + * Creates a closed range with the specified minimum and maximum values (both inclusive). + * + *

    + * The range uses the natural ordering of the elements to determine where values lie in the range. + *

    + * + *

    + * The arguments may be passed in the order (min, max) or (max,min). The getMinimum and getMaximum methods will return the correct values. + *

    + * + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + */ + public static IntegerRange of(final int fromInclusive, final int toInclusive) { + return of(Integer.valueOf(fromInclusive), Integer.valueOf(toInclusive)); + } + + /** + * Creates a closed range with the specified minimum and maximum values (both inclusive). + * + *

    + * The range uses the natural ordering of the elements to determine where values lie in the range. + *

    + * + *

    + * The arguments may be passed in the order (min, max) or (max,min). The getMinimum and getMaximum methods will return the correct values. + *

    + * + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + * @throws NullPointerException if either element is null. + */ + public static IntegerRange of(final Integer fromInclusive, final Integer toInclusive) { + return new IntegerRange(fromInclusive, toInclusive); + } + + /** + * Creates a new instance. + * + * @param number1 The first element, not null. + * @param number2 The second element, not null. + * @throws NullPointerException when element1 is null. + * @throws NullPointerException when element2 is null. + */ + private IntegerRange(final Integer number1, final Integer number2) { + super(number1, number2, null); + } + + /** + * Fits the given value into this range by returning the given value or, if out of bounds, the range minimum if + * below, or the range maximum if above. + * + *
    {@code
    +     * IntegerRange range = IntegerRange.of(16, 64);
    +     * range.fit(-9) -->  16
    +     * range.fit(0)  -->  16
    +     * range.fit(15) -->  16
    +     * range.fit(16) -->  16
    +     * range.fit(17) -->  17
    +     * ...
    +     * range.fit(63) -->  63
    +     * range.fit(64) -->  64
    +     * range.fit(99) -->  64
    +     * }
    + * + * @param element The element to test. + * @return The minimum, the element, or the maximum depending on the element's location relative to the range. + * @since 3.19.0 + */ + public int fit(final int element) { + return super.fit(element).intValue(); + } + + /** + * Returns a sequential ordered {@code IntStream} from {@link #getMinimum()} (inclusive) to {@link #getMaximum()} (inclusive) by an incremental step of + * {@code 1}. + * + * @return A sequential {@code IntStream} for the range of {@code int} elements. + * @since 3.18.0 + */ + public IntStream toIntStream() { + return IntStream.rangeClosed(getMinimum(), getMaximum()); + } +} diff --git a/src/main/java/org/apache/commons/lang3/JavaVersion.java b/src/main/java/org/apache/commons/lang3/JavaVersion.java index e287c9ffec8..dc4f2a88e00 100644 --- a/src/main/java/org/apache/commons/lang3/JavaVersion.java +++ b/src/main/java/org/apache/commons/lang3/JavaVersion.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,9 +19,8 @@ import org.apache.commons.lang3.math.NumberUtils; /** - *

    An enum representing all the versions of the Java specification. - * This is intended to mirror available values from the - * java.specification.version System property.

    + * Enumerates all known versions of the Java specification. This is intended to mirror available values from the java.specification.version System + * property. * * @since 3.0 */ @@ -75,211 +74,356 @@ public enum JavaVersion { /** * Java 1.9. * - * @deprecated As of release 3.5, replaced by {@link #JAVA_9} + * @deprecated As of release 3.5, replaced by {@link #JAVA_9}. */ @Deprecated JAVA_1_9(9.0f, "9"), /** - * Java 9 + * Java 9. * * @since 3.5 */ JAVA_9(9.0f, "9"), /** - * Java 10 + * Java 10. * * @since 3.7 */ JAVA_10(10.0f, "10"), /** - * Java 11 + * Java 11. * * @since 3.8 */ JAVA_11(11.0f, "11"), /** - * Java 12 + * Java 12. * * @since 3.9 */ JAVA_12(12.0f, "12"), /** - * Java 13 + * Java 13. * * @since 3.9 */ JAVA_13(13.0f, "13"), /** - * The most recent java version. Mainly introduced to avoid to break when a new version of Java is used. + * Java 14. + * + * @since 3.11 */ - JAVA_RECENT(maxVersion(), Float.toString(maxVersion())); + JAVA_14(14.0f, "14"), /** - * The float value. + * Java 15. + * + * @since 3.11 */ - private final float value; + JAVA_15(15.0f, "15"), /** - * The standard name. + * Java 16. + * + * @since 3.11 */ - private final String name; + JAVA_16(16.0f, "16"), /** - * Constructor. + * Java 17. * - * @param value the float value - * @param name the standard name, not null + * @since 3.12.0 */ - JavaVersion(final float value, final String name) { - this.value = value; - this.name = name; - } + JAVA_17(17.0f, "17"), - //----------------------------------------------------------------------- /** - *

    Whether this version of Java is at least the version of Java passed in.

    + * Java 18. * - *

    For example:
    - * {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}

    + * @since 3.13.0 + */ + JAVA_18(18.0f, "18"), + + /** + * Java 19. * - * @param requiredVersion the version to check against, not null - * @return true if this version is equal to or greater than the specified version + * @since 3.13.0 */ - public boolean atLeast(final JavaVersion requiredVersion) { - return this.value >= requiredVersion.value; - } + JAVA_19(19.0f, "19"), - //----------------------------------------------------------------------- /** - *

    Whether this version of Java is at most the version of Java passed in.

    + * Java 20. * - *

    For example:
    - * {@code myVersion.atMost(JavaVersion.JAVA_1_4)}

    + * @since 3.13.0 + */ + JAVA_20(20, "20"), + + /** + * Java 21. * - * @param requiredVersion the version to check against, not null - * @return true if this version is equal to or greater than the specified version - * @since 3.9 + * @since 3.13.0 */ - public boolean atMost(final JavaVersion requiredVersion) { - return this.value <= requiredVersion.value; - } + JAVA_21(21, "21"), /** - * Transforms the given string with a Java version number to the - * corresponding constant of this enumeration class. This method is used - * internally. + * Java 22. * - * @param nom the Java version as string - * @return the corresponding enumeration constant or null if the - * version is unknown + * @since 3.15.0 */ - // helper for static importing - static JavaVersion getJavaVersion(final String nom) { - return get(nom); - } + JAVA_22(22, "22"), + + /** + * Java 23. + * + * @since 3.18.0 + */ + JAVA_23(23, "23"), + + /** + * Java 24. + * + * @since 3.18.0 + */ + JAVA_24(24, "24"), /** - * Transforms the given string with a Java version number to the - * corresponding constant of this enumeration class. This method is used - * internally. + * Java 25. * - * @param nom the Java version as string - * @return the corresponding enumeration constant or null if the - * version is unknown + * @since 3.20.0 */ - static JavaVersion get(final String nom) { - if (nom == null) { + JAVA_25(25, "25"), + + /** + * Java 26. + * + * @since 3.20.0 + */ + JAVA_26(26, "26"), + + /** + * Java 27. + * + * @since 3.21.0 + */ + JAVA_27(27, "27"), + + /** + * The most recent Java version. Mainly introduced to avoid to break when a new version of Java is used. + */ + JAVA_RECENT(maxVersion(), Float.toString(maxVersion())); + + /** + * Transforms the given string with a Java version number to the corresponding constant of this enumeration class. This method is used internally. + * + * @param versionStr The Java version as string. + * @return The corresponding enumeration constant or {@code null} if the version is unknown. + */ + static JavaVersion get(final String versionStr) { + if (versionStr == null) { return null; - } else if ("0.9".equals(nom)) { + } + switch (versionStr) { + case "0.9": return JAVA_0_9; - } else if ("1.1".equals(nom)) { + case "1.1": return JAVA_1_1; - } else if ("1.2".equals(nom)) { + case "1.2": return JAVA_1_2; - } else if ("1.3".equals(nom)) { + case "1.3": return JAVA_1_3; - } else if ("1.4".equals(nom)) { + case "1.4": return JAVA_1_4; - } else if ("1.5".equals(nom)) { + case "1.5": return JAVA_1_5; - } else if ("1.6".equals(nom)) { + case "1.6": return JAVA_1_6; - } else if ("1.7".equals(nom)) { + case "1.7": return JAVA_1_7; - } else if ("1.8".equals(nom)) { + case "1.8": return JAVA_1_8; - } else if ("9".equals(nom)) { + case "9": return JAVA_9; - } else if ("10".equals(nom)) { + case "10": return JAVA_10; - } else if ("11".equals(nom)) { + case "11": return JAVA_11; - } else if ("12".equals(nom)) { + case "12": return JAVA_12; - } else if ("13".equals(nom)) { + case "13": return JAVA_13; - } - final float v = toFloatVersion(nom); - if ((v - 1.) < 1.) { // then we need to check decimals > .9 - final int firstComma = Math.max(nom.indexOf('.'), nom.indexOf(',')); - final int end = Math.max(nom.length(), nom.indexOf(',', firstComma)); - if (Float.parseFloat(nom.substring(firstComma + 1, end)) > .9f) { + case "14": + return JAVA_14; + case "15": + return JAVA_15; + case "16": + return JAVA_16; + case "17": + return JAVA_17; + case "18": + return JAVA_18; + case "19": + return JAVA_19; + case "20": + return JAVA_20; + case "21": + return JAVA_21; + case "22": + return JAVA_22; + case "23": + return JAVA_23; + case "24": + return JAVA_24; + case "25": + return JAVA_25; + case "26": + return JAVA_26; + case "27": + return JAVA_27; + default: + final float v = toFloatVersion(versionStr); + if (v - 1. < 1.) { // then we need to check decimals > .9 + final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(',')); + // read up to the next separator if present, otherwise to the end of the string + // (this was previously an inverted Math.max that always selected the full string) + int end = versionStr.indexOf(',', firstComma + 1); + if (end == -1) { + end = versionStr.length(); + } + try { + if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) { + return JAVA_RECENT; + } + } catch (final NumberFormatException e) { + // malformed version string ("1.", "bogus"): the documented contract is to return null + // for unknown versions rather than propagate an exception, which would otherwise poison + // SystemUtils' static initializer for the class loader lifetime. + return null; + } + } else if (v > 10) { return JAVA_RECENT; } - } else if (v > 10) { - return JAVA_RECENT; + return null; } - return null; } - //----------------------------------------------------------------------- /** - *

    The string value is overridden to return the standard name.

    - * - *

    For example, {@code "1.5"}.

    + * Transforms the given string with a Java version number to the corresponding constant of this enumeration class. This method is used internally. * - * @return the name, not null + * @param versionStr The Java version as string. + * @return The corresponding enumeration constant or {@code null} if the version is unknown. */ - @Override - public String toString() { - return name; + static JavaVersion getJavaVersion(final String versionStr) { + return get(versionStr); } /** * Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set. * - * @return the value of {@code java.specification.version} system property or 99.0 if it is not set. + * @return The value of {@code java.specification.version} system property or 99.0 if it is not set. */ private static float maxVersion() { - final float v = toFloatVersion(System.getProperty("java.specification.version", "99.0")); - if (v > 0) { - return v; - } - return 99f; + final float v = toFloatVersion(SystemProperties.getJavaSpecificationVersion("99.0")); + return v > 0 ? v : 99f; + } + + static String[] split(final String value) { + return RegExUtils.VERSION_SPLIT_PATTERN.split(value); } /** * Parses a float value from a String. * - * @param value the String to parse. - * @return the float value represented by the string or -1 if the given String can not be parsed. + * @param value The String to parse. + * @return The float value represented by the string or -1 if the given String cannot be parsed. */ private static float toFloatVersion(final String value) { final int defaultReturnValue = -1; - if (value.contains(".")) { - final String[] toParse = value.split("\\."); - if (toParse.length >= 2) { - return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue); - } - } else { + if (!value.contains(".")) { return NumberUtils.toFloat(value, defaultReturnValue); } + final String[] toParse = split(value); + if (toParse.length >= 2) { + return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue); + } return defaultReturnValue; } + + /** + * The float value. + */ + private final float value; + + /** + * The standard name. + */ + private final String name; + + /** + * Constructs a new instance. + * + * @param value The float value. + * @param name The standard name, not null. + */ + JavaVersion(final float value, final String name) { + this.value = value; + this.name = name; + } + + /** + * Tests whether this version of Java is at least the version of Java passed in. + * + *

    + * For example: + *

    + * + *
    +     *  {@code
    +     * myVersion.atLeast(JavaVersion.JAVA_1_8)
    +     * }
    + * + * @param requiredVersion The version to check against, not null. + * @return true if this version is equal to or greater than the specified version. + */ + public boolean atLeast(final JavaVersion requiredVersion) { + return this.value >= requiredVersion.value; + } + + /** + * Tests whether this version of Java is at most the version of Java passed in. + * + *

    + * For example: + *

    + * + *
    +     *  {@code
    +     * myVersion.atMost(JavaVersion.JAVA_1_4)
    +     * }
    + * + * @param requiredVersion The version to check against, not null. + * @return true if this version is equal to or less than the specified version. + * @since 3.9 + */ + public boolean atMost(final JavaVersion requiredVersion) { + return this.value <= requiredVersion.value; + } + + /** + * The string value is overridden to return the standard name. + * + *

    + * For example, {@code "1.5"}. + *

    + * + * @return The name, not null. + */ + @Override + public String toString() { + return name; + } } diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java b/src/main/java/org/apache/commons/lang3/LocaleUtils.java index 703b33613fe..33b0c12b159 100644 --- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java +++ b/src/main/java/org/apache/commons/lang3/LocaleUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,223 +14,306 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.commons.lang3; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; +import java.util.Comparator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** - *

    Operations to assist when working with a {@link Locale}.

    + * Operations to assist when working with a {@link Locale}. * - *

    This class tries to handle {@code null} input gracefully. - * An exception will not be thrown for a {@code null} input. - * Each method documents its behaviour in more detail.

    + *

    + * This class tries to handle {@code null} input gracefully. An exception will not be thrown for a {@code null} input. Each method documents its behavior in + * more detail. + *

    * + * @see Locale * @since 2.2 */ public class LocaleUtils { - /** Concurrent map of language locales by country. */ - private static final ConcurrentMap> cLanguagesByCountry = - new ConcurrentHashMap<>(); + /** + * Avoids synchronization, initializes on demand. + */ + private static final class SyncAvoid { + + /** Private unmodifiable and sorted list of available locales. */ + private static final List AVAILABLE_LOCALE_ULIST; + + /** Private unmodifiable set of available locales. */ + private static final Set AVAILABLE_LOCALE_USET; + static { + AVAILABLE_LOCALE_ULIST = Collections + .unmodifiableList(Arrays.asList(ArraySorter.sort(Locale.getAvailableLocales(), Comparator.comparing(Locale::toString)))); + AVAILABLE_LOCALE_USET = Collections.unmodifiableSet(new LinkedHashSet<>(AVAILABLE_LOCALE_ULIST)); + } + } - /** Concurrent map of country locales by language. */ - private static final ConcurrentMap> cCountriesByLanguage = - new ConcurrentHashMap<>(); + /** + * The underscore character {@code '}{@value}{@code '}. + */ + private static final char UNDERSCORE = '_'; /** - *

    {@code LocaleUtils} instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code LocaleUtils.toLocale("en_GB");}.

    + * The undetermined language {@value}. + *

    + * If a language is empty, or not well-formed (for example "a" or "e2"), {@link Locale#toLanguageTag()} will return {@code "und"} (Undetermined). + *

    * - *

    This constructor is public to permit tools that require a JavaBean instance - * to operate.

    + * @see Locale#toLanguageTag() */ - public LocaleUtils() { - super(); - } + private static final String UNDETERMINED = "und"; + + /** + * The dash character {@code '}{@value}{@code '}. + */ + private static final char DASH = '-'; + + /** + * Concurrent map of language locales by country. + */ + private static final ConcurrentMap> ccToLocalesMap = new ConcurrentHashMap<>(); + + + /** + * Concurrent map of country locales by language. + */ + private static final ConcurrentMap> lcToLocalesMap = new ConcurrentHashMap<>(); - //----------------------------------------------------------------------- /** - *

    Converts a String to a Locale.

    + * Gets an unmodifiable and sorted list of installed locales. * - *

    This method takes the string format of a locale and creates the - * locale object from it.

    + *

    + * This method is a wrapper around {@link Locale#getAvailableLocales()}. It is more efficient, as the JDK method must create a new array each time it is + * called. + *

    * - *
    -     *   LocaleUtils.toLocale("")           = new Locale("", "")
    -     *   LocaleUtils.toLocale("en")         = new Locale("en", "")
    -     *   LocaleUtils.toLocale("en_GB")      = new Locale("en", "GB")
    -     *   LocaleUtils.toLocale("en_001")     = new Locale("en", "001")
    -     *   LocaleUtils.toLocale("en_GB_xxx")  = new Locale("en", "GB", "xxx")   (#)
    -     * 
    + * @return The unmodifiable and sorted list of available locales. + */ + public static List availableLocaleList() { + return SyncAvoid.AVAILABLE_LOCALE_ULIST; + } + + private static List availableLocaleList(final Predicate predicate) { + return availableLocaleList().stream().filter(predicate).collect(Collectors.toList()); + } + + /** + * Gets an unmodifiable set of installed locales. * - *

    (#) The behaviour of the JDK variant constructor changed between JDK1.3 and JDK1.4. - * In JDK1.3, the constructor upper cases the variant, in JDK1.4, it doesn't. - * Thus, the result from getVariant() may vary depending on your JDK.

    + *

    + * This method is a wrapper around {@link Locale#getAvailableLocales()}. It is more efficient, as the JDK method must create a new array each time it is + * called. + *

    * - *

    This method validates the input strictly. - * The language code must be lowercase. - * The country code must be uppercase. - * The separator must be an underscore. - * The length must be correct. + * @return The unmodifiable set of available locales. + */ + public static Set availableLocaleSet() { + return SyncAvoid.AVAILABLE_LOCALE_USET; + } + + /** + * Gets the list of countries supported for a given language. + * + *

    + * This method takes a language code and searches to find the countries available for that language. Variant locales are removed. *

    * - * @param str the locale String to convert, null returns null - * @return a Locale, null if null input - * @throws IllegalArgumentException if the string is an invalid format - * @see Locale#forLanguageTag(String) + * @param languageCode The 2 letter language code, null returns empty. + * @return An unmodifiable List of Locale objects, not null. */ - public static Locale toLocale(final String str) { - if (str == null) { - return null; - } - if (str.isEmpty()) { // LANG-941 - JDK 8 introduced an empty locale where all fields are blank - return new Locale(StringUtils.EMPTY, StringUtils.EMPTY); - } - if (str.contains("#")) { // LANG-879 - Cannot handle Java 7 script & extensions - throw new IllegalArgumentException("Invalid locale format: " + str); - } - final int len = str.length(); - if (len < 2) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - final char ch0 = str.charAt(0); - if (ch0 == '_') { - if (len < 3) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - final char ch1 = str.charAt(1); - final char ch2 = str.charAt(2); - if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - if (len == 3) { - return new Locale(StringUtils.EMPTY, str.substring(1, 3)); - } - if (len < 5) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - if (str.charAt(3) != '_') { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - return new Locale(StringUtils.EMPTY, str.substring(1, 3), str.substring(4)); + public static List countriesByLanguage(final String languageCode) { + // Only syntactically valid ISO 639 codes can match an available locale's language; anything + // else is answered without touching the cache so that arbitrary caller strings are never + // retained for the lifetime of the class loader. + if (languageCode == null || !languageCode.isEmpty() && !isISO639LanguageCode(languageCode)) { + return Collections.emptyList(); } + return lcToLocalesMap.computeIfAbsent(languageCode, lc -> Collections + .unmodifiableList(availableLocaleList(locale -> languageCode.equals(locale.getLanguage()) && !hasCountry(locale) && hasVariant(locale)))); + } - return parseLocale(str); + static ConcurrentMap> getCcToLocalesMap() { + return ccToLocalesMap; } /** - * Tries to parse a locale from the given String. + * Gets the cache of country locales by language. * - * @param str the String to parse a locale from. - * @return a Locale instance parsed from the given String. - * @throws IllegalArgumentException if the given String can not be parsed. + * @return the cache of country locales by language. */ - private static Locale parseLocale(final String str) { - if (isISO639LanguageCode(str)) { - return new Locale(str); - } + static ConcurrentMap> getLcToLocalesMap() { + return lcToLocalesMap; + } - final String[] segments = str.split("_", -1); - final String language = segments[0]; - if (segments.length == 2) { - final String country = segments[1]; - if (isISO639LanguageCode(language) && isISO3166CountryCode(country) || - isNumericAreaCode(country)) { - return new Locale(language, country); - } - } else if (segments.length == 3) { - final String country = segments[1]; - final String variant = segments[2]; - if (isISO639LanguageCode(language) && - (country.isEmpty() || isISO3166CountryCode(country) || isNumericAreaCode(country)) && - !variant.isEmpty()) { - return new Locale(language, country, variant); - } - } - throw new IllegalArgumentException("Invalid locale format: " + str); + /** + * Tests whether the given Locale defines a variant. + * + * @param locale The Locale to test. + * @return whether the given Locale defines a variant. + */ + private static boolean hasCountry(final Locale locale) { + return locale.getCountry().isEmpty(); } /** - * Checks whether the given String is a ISO 639 compliant language code. + * Tests whether the given Locale defines a country. * - * @param str the String to check. - * @return true, if the given String is a ISO 639 compliant language code. + * @param locale The Locale to test. + * @return whether the given Locale defines a country. */ - private static boolean isISO639LanguageCode(final String str) { - return StringUtils.isAllLowerCase(str) && (str.length() == 2 || str.length() == 3); + private static boolean hasVariant(final Locale locale) { + return locale.getVariant().isEmpty(); } /** - * Checks whether the given String is a ISO 3166 alpha-2 country code. + * Tests whether the given string is the length of an ISO 3166 alpha-2 country code. * - * @param str the String to check - * @return true, is the given String is a ISO 3166 compliant country code. + * @param str The string to test. + * @return whether the given string is the length of an ISO 3166 alpha-2 country code. + */ + private static boolean isAlpha2Len(final String str) { + return str.length() == 2; + } + + /** + * Tests whether the given string is the length of an ISO 3166 alpha-3 country code. + * + * @param str The string to test. + * @return whether the given string is the length of an ISO 3166 alpha-3 country code. + */ + private static boolean isAlpha3Len(final String str) { + return str.length() == 3; + } + + /** + * Checks if the locale specified is in the set of available locales. + * + * @param locale The Locale object to check if it is available. + * @return true if the locale is a known locale. + */ + public static boolean isAvailableLocale(final Locale locale) { + return availableLocaleSet().contains(locale); + } + + /** + * Tests whether the given String is a ISO 3166 alpha-2 country code. + * + * @param str The String to check. + * @return true, is the given String is a ISO 3166 compliant country code. */ private static boolean isISO3166CountryCode(final String str) { - return StringUtils.isAllUpperCase(str) && str.length() == 2; + return StringUtils.isAllUpperCase(str) && isAlpha2Len(str); } /** - * Checks whether the given String is a UN M.49 numeric area code. + * Tests whether the given String is a ISO 639 compliant language code. + * + * @param str The String to check. + * @return true, if the given String is a ISO 639 compliant language code. + */ + private static boolean isISO639LanguageCode(final String str) { + return StringUtils.isAllLowerCase(str) && (isAlpha2Len(str) || isAlpha3Len(str)); + } + + /** + * Tests whether a Locale's language is undetermined. + *

    + * A Locale's language tag is undetermined if it's value is {@code "und"}. If a language is empty, or not well-formed (for example, "a" or "e2"), it will be + * equal to {@code "und"}. + *

    * - * @param str the String to check + * @param locale The locale to test. + * @return whether a Locale's language is undetermined. + * @see Locale#toLanguageTag() + * @since 3.14.0 + */ + public static boolean isLanguageUndetermined(final Locale locale) { + return locale == null || UNDETERMINED.equals(locale.toLanguageTag()); + } + + /** + * TestsNo whether the given String is a UN M.49 numeric area code. + * + * @param str The String to check. * @return true, is the given String is a UN M.49 numeric area code. */ private static boolean isNumericAreaCode(final String str) { - return StringUtils.isNumeric(str) && str.length() == 3; + return StringUtils.isNumeric(str) && isAlpha3Len(str); + } + + /** + * Obtains the list of languages supported for a given country. + * + *

    + * This method takes a country code and searches to find the languages available for that country. Variant locales are removed. + *

    + * + * @param countryCode The 2-letter country code, null returns empty. + * @return An unmodifiable List of Locale objects, not null. + */ + public static List languagesByCountry(final String countryCode) { + // Only syntactically valid ISO 3166 alpha-2 / UN M.49 numeric codes can match an available + // locale's country; anything else is answered without touching the cache so that arbitrary + // caller strings are never retained for the lifetime of the class loader. + if (countryCode == null || !countryCode.isEmpty() && !isISO3166CountryCode(countryCode) && !isNumericAreaCode(countryCode)) { + return Collections.emptyList(); + } + return ccToLocalesMap.computeIfAbsent(countryCode, + k -> Collections.unmodifiableList(availableLocaleList(locale -> countryCode.equals(locale.getCountry()) && hasVariant(locale)))); } - //----------------------------------------------------------------------- /** - *

    Obtains the list of locales to search through when performing - * a locale search.

    + * Obtains the list of locales to search through when performing a locale search. * *
          * localeLookupList(Locale("fr", "CA", "xxx"))
          *   = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr")]
          * 
    * - * @param locale the locale to start from - * @return the unmodifiable list of Locale objects, 0 being locale, not null + * @param locale The locale to start from. + * @return The unmodifiable list of Locale objects, 0 being locale, not null. */ public static List localeLookupList(final Locale locale) { return localeLookupList(locale, locale); } - //----------------------------------------------------------------------- /** - *

    Obtains the list of locales to search through when performing - * a locale search.

    + * Obtains the list of locales to search through when performing a locale search. * *
          * localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
          *   = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr"), Locale("en"]
          * 
    * - *

    The result list begins with the most specific locale, then the - * next more general and so on, finishing with the default locale. - * The list will never contain the same locale twice.

    + *

    + * The result list begins with the most specific locale, then the next more general and so on, finishing with the default locale. The list will never + * contain the same locale twice. + *

    * - * @param locale the locale to start from, null returns empty list - * @param defaultLocale the default locale to use if no other is found - * @return the unmodifiable list of Locale objects, 0 being locale, not null + * @param locale The locale to start from, null returns empty list. + * @param defaultLocale The default locale to use if no other is found. + * @return The unmodifiable list of Locale objects, 0 being locale, not null. */ public static List localeLookupList(final Locale locale, final Locale defaultLocale) { final List list = new ArrayList<>(4); if (locale != null) { list.add(locale); - if (!locale.getVariant().isEmpty()) { + if (!hasVariant(locale)) { list.add(new Locale(locale.getLanguage(), locale.getCountry())); } - if (!locale.getCountry().isEmpty()) { + if (!hasCountry(locale)) { list.add(new Locale(locale.getLanguage(), StringUtils.EMPTY)); } if (!list.contains(defaultLocale)) { @@ -240,121 +323,155 @@ public static List localeLookupList(final Locale locale, final Locale de return Collections.unmodifiableList(list); } - //----------------------------------------------------------------------- /** - *

    Obtains an unmodifiable list of installed locales.

    - * - *

    This method is a wrapper around {@link Locale#getAvailableLocales()}. - * It is more efficient, as the JDK method must create a new array each - * time it is called.

    + * Creates new {@linkplain Locale} for the given country. * - * @return the unmodifiable list of available locales + * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code. See the {@linkplain Locale} class description about valid country + * values. + * @throws NullPointerException thrown if either argument is null. + * @return A new new Locale for the given country. + * @see Locale#Locale(String, String) */ - public static List availableLocaleList() { - return SyncAvoid.AVAILABLE_LOCALE_LIST; + static Locale ofCountry(final String country) { + return new Locale(StringUtils.EMPTY, country); } - //----------------------------------------------------------------------- /** - *

    Obtains an unmodifiable set of installed locales.

    - * - *

    This method is a wrapper around {@link Locale#getAvailableLocales()}. - * It is more efficient, as the JDK method must create a new array each - * time it is called.

    + * Tries to parse a Locale from the given String. + *

    + * See {@link Locale} for the format. + *

    * - * @return the unmodifiable set of available locales + * @param str The String to parse as a Locale. + * @return A Locale parsed from the given String. + * @throws IllegalArgumentException if the given String cannot be parsed. + * @see Locale + * @see Locale special cases */ - public static Set availableLocaleSet() { - return SyncAvoid.AVAILABLE_LOCALE_SET; + private static Locale parseLocale(final String str) { + if (isISO639LanguageCode(str)) { + return new Locale(str); + } + final int limit = 3; + final char separator = str.indexOf(UNDERSCORE) != -1 ? UNDERSCORE : DASH; + final String[] segments = str.split(String.valueOf(separator), 3); + final String language = segments[0]; + if (segments.length == 2) { + final String country = segments[1]; + if (isISO639LanguageCode(language) && (isISO3166CountryCode(country) || isNumericAreaCode(country))) { + return new Locale(language, country); + } + } else if (segments.length == limit) { + final String country = segments[1]; + final String variant = segments[2]; + // Special case 1: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor + if (str.equals("th_TH_TH_#u-nu-thai")) { + return new Locale(language, country, "TH"); + } + // Special case 2: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor + if (str.equals("ja_JP_JP_#u-ca-japanese")) { + return new Locale(language, country, "JP"); + } + if (isISO639LanguageCode(language) && (country.isEmpty() || isISO3166CountryCode(country) || isNumericAreaCode(country)) && !variant.isEmpty()) { + return new Locale(language, country, variant); + } + } + if (ArrayUtils.contains(Locale.getISOCountries(), str)) { + return new Locale(StringUtils.EMPTY, str); + } + throw new IllegalArgumentException("Invalid locale format: " + str); } - //----------------------------------------------------------------------- /** - *

    Checks if the locale specified is in the list of available locales.

    + * Returns the given locale if non-{@code null}, otherwise {@link Locale#getDefault()}. * - * @param locale the Locale object to check if it is available - * @return true if the locale is a known locale + * @param locale A locale or {@code null}. + * @return The given locale if non-{@code null}, otherwise {@link Locale#getDefault()}. + * @since 3.12.0 */ - public static boolean isAvailableLocale(final Locale locale) { - return availableLocaleList().contains(locale); + public static Locale toLocale(final Locale locale) { + return locale != null ? locale : Locale.getDefault(); } - //----------------------------------------------------------------------- /** - *

    Obtains the list of languages supported for a given country.

    + * Converts a String to a Locale. * - *

    This method takes a country code and searches to find the - * languages available for that country. Variant locales are removed.

    + *

    + * This method takes the string format of a locale and creates the locale object from it. + *

    + * + *
    +     *   LocaleUtils.toLocale("")           = new Locale("", "")
    +     *   LocaleUtils.toLocale("en")         = new Locale("en", "")
    +     *   LocaleUtils.toLocale("en_GB")      = new Locale("en", "GB")
    +     *   LocaleUtils.toLocale("en-GB")      = new Locale("en", "GB")
    +     *   LocaleUtils.toLocale("en_001")     = new Locale("en", "001")
    +     *   LocaleUtils.toLocale("en_GB_xxx")  = new Locale("en", "GB", "xxx")   (#)
    +     *   LocaleUtils.toLocale("US")         = new Locale("", "US") // Because "US" is Locale.getISOCountries()
    +     * 
    + * + *

    + * (#) The behavior of the JDK variant constructor changed between JDK1.3 and JDK1.4. In JDK1.3, the constructor upper cases the variant, in JDK1.4, it + * doesn't. Thus, the result from getVariant() may vary depending on your JDK. + *

    * - * @param countryCode the 2 letter country code, null returns empty - * @return an unmodifiable List of Locale objects, not null + *

    + * This method validates the input strictly. The language code must be lowercase. The country code must be uppercase. The separator must be an underscore or + * a dash. The length must be correct. + *

    + * + * @param str The locale String to convert, null returns null. + * @return A Locale, null if null input. + * @throws IllegalArgumentException if the string is an invalid format. + * @see Locale#forLanguageTag(String) + * @see Locale#getISOCountries() + * @see Locale special cases */ - public static List languagesByCountry(final String countryCode) { - if (countryCode == null) { - return Collections.emptyList(); + public static Locale toLocale(final String str) { + if (str == null) { + // TODO Should this return the default locale? + return null; } - List langs = cLanguagesByCountry.get(countryCode); - if (langs == null) { - langs = new ArrayList<>(); - final List locales = availableLocaleList(); - for (final Locale locale : locales) { - if (countryCode.equals(locale.getCountry()) && - locale.getVariant().isEmpty()) { - langs.add(locale); - } + if (str.isEmpty()) { // LANG-941 - JDK 8 introduced an empty locale where all fields are blank + return new Locale(StringUtils.EMPTY, StringUtils.EMPTY); + } + final int len = str.length(); + if (len < 2) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + final char ch0 = str.charAt(0); + if (ch0 == UNDERSCORE || ch0 == DASH) { + if (len < 3) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + final char ch1 = str.charAt(1); + final char ch2 = str.charAt(2); + if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) { + throw new IllegalArgumentException("Invalid locale format: " + str); } - langs = Collections.unmodifiableList(langs); - cLanguagesByCountry.putIfAbsent(countryCode, langs); - langs = cLanguagesByCountry.get(countryCode); + if (len == 3) { + return new Locale(StringUtils.EMPTY, str.substring(1, 3)); + } + if (len < 5 || str.charAt(3) != ch0) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + return new Locale(StringUtils.EMPTY, str.substring(1, 3), str.substring(4)); } - return langs; + return parseLocale(str); } - //----------------------------------------------------------------------- /** - *

    Obtains the list of countries supported for a given language.

    + * {@link LocaleUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as + * {@code LocaleUtils.toLocale("en_GB");}. * - *

    This method takes a language code and searches to find the - * countries available for that language. Variant locales are removed.

    + *

    + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

    * - * @param languageCode the 2 letter language code, null returns empty - * @return an unmodifiable List of Locale objects, not null + * @deprecated TODO Make private in 4.0. */ - public static List countriesByLanguage(final String languageCode) { - if (languageCode == null) { - return Collections.emptyList(); - } - List countries = cCountriesByLanguage.get(languageCode); - if (countries == null) { - countries = new ArrayList<>(); - final List locales = availableLocaleList(); - for (final Locale locale : locales) { - if (languageCode.equals(locale.getLanguage()) && - !locale.getCountry().isEmpty() && - locale.getVariant().isEmpty()) { - countries.add(locale); - } - } - countries = Collections.unmodifiableList(countries); - cCountriesByLanguage.putIfAbsent(languageCode, countries); - countries = cCountriesByLanguage.get(languageCode); - } - return countries; - } - - //----------------------------------------------------------------------- - // class to avoid synchronization (Init on demand) - static class SyncAvoid { - /** Unmodifiable list of available locales. */ - private static final List AVAILABLE_LOCALE_LIST; - /** Unmodifiable set of available locales. */ - private static final Set AVAILABLE_LOCALE_SET; - - static { - final List list = new ArrayList<>(Arrays.asList(Locale.getAvailableLocales())); // extra safe - AVAILABLE_LOCALE_LIST = Collections.unmodifiableList(list); - AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet<>(list)); - } + @Deprecated + public LocaleUtils() { + // empty } - } diff --git a/src/main/java/org/apache/commons/lang3/LongRange.java b/src/main/java/org/apache/commons/lang3/LongRange.java new file mode 100644 index 00000000000..aa5abd72f74 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/LongRange.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.util.stream.LongStream; + +/** + * Specializes {@link NumberRange} for {@link Long}s. + * + *

    + * This class is not designed to interoperate with other NumberRanges + *

    + * + * @since 3.13.0 + */ +public final class LongRange extends NumberRange { + + private static final long serialVersionUID = 1L; + + /** + * Creates a closed range with the specified minimum and maximum values (both inclusive). + * + *

    + * The range uses the natural ordering of the elements to determine where values lie in the range. + *

    + * + *

    + * The arguments may be passed in the order (min, max) or (max,min). The getMinimum and getMaximum methods will return the correct values. + *

    + * + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + */ + public static LongRange of(final long fromInclusive, final long toInclusive) { + return of(Long.valueOf(fromInclusive), Long.valueOf(toInclusive)); + } + + /** + * Creates a closed range with the specified minimum and maximum values (both inclusive). + * + *

    + * The range uses the natural ordering of the elements to determine where values lie in the range. + *

    + * + *

    + * The arguments may be passed in the order (min, max) or (max,min). The getMinimum and getMaximum methods will return the correct values. + *

    + * + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + * @throws NullPointerException if either element is null. + */ + public static LongRange of(final Long fromInclusive, final Long toInclusive) { + return new LongRange(fromInclusive, toInclusive); + } + + /** + * Creates a new instance. + * + * @param number1 The first element, not null. + * @param number2 The second element, not null. + * @throws NullPointerException when element1 is null. + * @throws NullPointerException when element2 is null. + */ + private LongRange(final Long number1, final Long number2) { + super(number1, number2, null); + } + + /** + * Fits the given value into this range by returning the given value or, if out of bounds, the range minimum if + * below, or the range maximum if above. + * + *
    {@code
    +     * LongRange range = LongRange.of(16, 64);
    +     * range.fit(-9) -->  16
    +     * range.fit(0)  -->  16
    +     * range.fit(15) -->  16
    +     * range.fit(16) -->  16
    +     * range.fit(17) -->  17
    +     * ...
    +     * range.fit(63) -->  63
    +     * range.fit(64) -->  64
    +     * range.fit(99) -->  64
    +     * }
    + * + * @param element The element to test. + * @return The minimum, the element, or the maximum depending on the element's location relative to the range. + * @since 3.19.0 + */ + public long fit(final long element) { + return super.fit(element).longValue(); + } + + /** + * Returns a sequential ordered {@code LongStream} from {@link #getMinimum()} (inclusive) to {@link #getMaximum()} (inclusive) by an incremental step of + * {@code 1}. + * + * @return A sequential {@code LongStream} for the range of {@code long} elements + * @since 3.18.0 + */ + public LongStream toLongStream() { + return LongStream.rangeClosed(getMinimum(), getMaximum()); + } + +} diff --git a/src/main/java/org/apache/commons/lang3/NotImplementedException.java b/src/main/java/org/apache/commons/lang3/NotImplementedException.java index 2ad7463a8fd..35ff23b7903 100644 --- a/src/main/java/org/apache/commons/lang3/NotImplementedException.java +++ b/src/main/java/org/apache/commons/lang3/NotImplementedException.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,13 +17,13 @@ package org.apache.commons.lang3; /** - *

    Thrown to indicate that a block of code has not been implemented. - * This exception supplements {@code UnsupportedOperationException} - * by providing a more semantically rich description of the problem.

    + * Thrown to indicate that a block of code has not been implemented. + * This exception supplements {@link UnsupportedOperationException} + * by providing a more semantically rich description of the problem. * - *

    {@code NotImplementedException} represents the case where the + *

    {@link NotImplementedException} represents the case where the * author has yet to implement the logic at this point in the program. - * This can act as an exception based TODO tag.

    + * This can act as an exception based TODO tag.

    * *
      * public void foo() {
    @@ -44,6 +44,7 @@ public class NotImplementedException extends UnsupportedOperationException {
     
         private static final long serialVersionUID = 20131021L;
     
    +    /** A resource for more information regarding the lack of implementation. */
         private final String code;
     
         /**
    @@ -68,11 +69,13 @@ public NotImplementedException(final String message) {
         /**
          * Constructs a NotImplementedException.
          *
    -     * @param cause cause of the exception
    +     * @param message description of the exception
    +     * @param code code indicating a resource for more information regarding the lack of implementation
          * @since 3.2
          */
    -    public NotImplementedException(final Throwable cause) {
    -        this(cause, null);
    +    public NotImplementedException(final String message, final String code) {
    +        super(message);
    +        this.code = code;
         }
     
         /**
    @@ -90,11 +93,12 @@ public NotImplementedException(final String message, final Throwable cause) {
          * Constructs a NotImplementedException.
          *
          * @param message description of the exception
    +     * @param cause cause of the exception
          * @param code code indicating a resource for more information regarding the lack of implementation
          * @since 3.2
          */
    -    public NotImplementedException(final String message, final String code) {
    -        super(message);
    +    public NotImplementedException(final String message, final Throwable cause, final String code) {
    +        super(message, cause);
             this.code = code;
         }
     
    @@ -102,24 +106,21 @@ public NotImplementedException(final String message, final String code) {
          * Constructs a NotImplementedException.
          *
          * @param cause cause of the exception
    -     * @param code code indicating a resource for more information regarding the lack of implementation
          * @since 3.2
          */
    -    public NotImplementedException(final Throwable cause, final String code) {
    -        super(cause);
    -        this.code = code;
    +    public NotImplementedException(final Throwable cause) {
    +        this(cause, null);
         }
     
         /**
          * Constructs a NotImplementedException.
          *
    -     * @param message description of the exception
          * @param cause cause of the exception
          * @param code code indicating a resource for more information regarding the lack of implementation
          * @since 3.2
          */
    -    public NotImplementedException(final String message, final Throwable cause, final String code) {
    -        super(message, cause);
    +    public NotImplementedException(final Throwable cause, final String code) {
    +        super(cause);
             this.code = code;
         }
     
    @@ -128,7 +129,7 @@ public NotImplementedException(final String message, final Throwable cause, fina
          * further information regarding the lack of implementation. It might, for example, be an issue
          * tracker ID or a URL.
          *
    -     * @return a code indicating a resource for more information regarding the lack of implementation
    +     * @return A code indicating a resource for more information regarding the lack of implementation
          */
         public String getCode() {
             return this.code;
    diff --git a/src/main/java/org/apache/commons/lang3/NumberRange.java b/src/main/java/org/apache/commons/lang3/NumberRange.java
    new file mode 100644
    index 00000000000..1e46992f229
    --- /dev/null
    +++ b/src/main/java/org/apache/commons/lang3/NumberRange.java
    @@ -0,0 +1,49 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package org.apache.commons.lang3;
    +
    +import java.util.Comparator;
    +
    +/**
    + * Specializes {@link Range} for {@link Number}s.
    + * 

    + * We only offer specializations for Integer, Long, and Double (like Java Streams). + *

    + * + * @param The Number class. + * @since 3.13.0 + */ +public class NumberRange extends Range { + + private static final long serialVersionUID = 1L; + + /** + * Creates an instance. + * + * @param number1 The first element, not null. + * @param number2 The second element, not null. + * @param comp The comparator to be used, null for natural ordering. + * @throws NullPointerException when element1 is null. + * @throws NullPointerException when element2 is null. + * @throws IllegalArgumentException when element1 or element2 is a floating-point NaN. + */ + public NumberRange(final N number1, final N number2, final Comparator comp) { + super(number1, number2, comp); + } + +} diff --git a/src/main/java/org/apache/commons/lang3/ObjectUtils.java b/src/main/java/org/apache/commons/lang3/ObjectUtils.java index b647c1a4a60..9f50f9fceb9 100644 --- a/src/main/java/org/apache/commons/lang3/ObjectUtils.java +++ b/src/main/java/org/apache/commons/lang3/ObjectUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,28 +19,41 @@ import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Hashtable; import java.util.Map; -import java.util.TreeSet; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Stream; import org.apache.commons.lang3.exception.CloneFailedException; +import org.apache.commons.lang3.function.Consumers; +import org.apache.commons.lang3.function.Suppliers; import org.apache.commons.lang3.mutable.MutableInt; +import org.apache.commons.lang3.stream.Streams; import org.apache.commons.lang3.text.StrBuilder; +import org.apache.commons.lang3.time.DurationUtils; /** - *

    Operations on {@code Object}.

    + * Operations on {@link Object}. * - *

    This class tries to handle {@code null} input gracefully. + *

    + * This class tries to handle {@code null} input gracefully. * An exception will generally not be thrown for a {@code null} input. - * Each method documents its behaviour in more detail.

    + * Each method documents its behavior in more detail. + *

    * *

    #ThreadSafe#

    + * + * @see Consumers + * @see Suppliers * @since 1.0 */ //@Immutable @@ -48,306 +61,487 @@ // because it is part of the signature of deprecated methods public class ObjectUtils { + /** + * Class used as a null placeholder where {@code null} has another meaning. + * + *

    + * For example, in a {@link HashMap} the {@link java.util.HashMap#get(Object)} method returns {@code null} if the {@link Map} contains {@code null} or if + * there is no matching key. The {@code null} placeholder can be used to distinguish between these two cases. + *

    + * + *

    + * Another example is {@link Hashtable}, where {@code null} cannot be stored. + *

    + */ + public static class Null implements Serializable { + + /** + * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0 + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 7092611880189329093L; + + /** + * Restricted constructor - singleton. + */ + Null() { + } + + /** + * Ensures singleton after serialization. + * + * @return The singleton value. + */ + private Object readResolve() { + return NULL; + } + } + private static final char AT_SIGN = '@'; /** - *

    Singleton used as a {@code null} placeholder where - * {@code null} has another meaning.

    + * Singleton used as a {@code null} placeholder where {@code null} has another meaning. * - *

    For example, in a {@code HashMap} the - * {@link java.util.HashMap#get(java.lang.Object)} method returns - * {@code null} if the {@code Map} contains {@code null} or if there - * is no matching key. The {@code Null} placeholder can be used to - * distinguish between these two cases.

    + *

    + * For example, in a {@link HashMap} the {@link java.util.HashMap#get(Object)} method returns {@code null} if the {@link Map} contains {@code null} or if + * there is no matching key. The {@code null} placeholder can be used to distinguish between these two cases. + *

    * - *

    Another example is {@code Hashtable}, where {@code null} - * cannot be stored.

    + *

    + * Another example is {@link Hashtable}, where {@code null} cannot be stored. + *

    * - *

    This instance is Serializable.

    + *

    + * This instance is Serializable. + *

    */ public static final Null NULL = new Null(); /** - *

    {@code ObjectUtils} instances should NOT be constructed in - * standard programming. Instead, the static methods on the class should - * be used, such as {@code ObjectUtils.defaultIfNull("a","b");}.

    + * Tests if all values in the array are not {@code nulls}. + * + *

    + * If any value is {@code null} or the array is {@code null} then {@code false} is returned. If all elements in array are not {@code null} or the array is + * empty (contains no elements) {@code true} is returned. + *

    + * + *
    +     * ObjectUtils.allNotNull(*)             = true
    +     * ObjectUtils.allNotNull(*, *)          = true
    +     * ObjectUtils.allNotNull(null)          = false
    +     * ObjectUtils.allNotNull(null, null)    = false
    +     * ObjectUtils.allNotNull(null, *)       = false
    +     * ObjectUtils.allNotNull(*, null)       = false
    +     * ObjectUtils.allNotNull(*, *, null, *) = false
    +     * 
    * - *

    This constructor is public to permit tools that require a JavaBean - * instance to operate.

    + * @param values The values to test, may be {@code null} or empty. + * @return {@code false} if there is at least one {@code null} value in the array or the array is {@code null}, {@code true} if all values in the array are + * not {@code null}s or array contains no elements. + * @since 3.5 */ - public ObjectUtils() { - super(); + public static boolean allNotNull(final Object... values) { + return values != null && Stream.of(values).noneMatch(Objects::isNull); } - // Empty checks - //----------------------------------------------------------------------- /** - *

    Checks if an Object is empty or null.

    + * Tests if all values in the given array are {@code null}. * - * The following types are supported: - *
      - *
    • {@link CharSequence}: Considered empty if its length is zero.
    • - *
    • {@code Array}: Considered empty if its length is zero.
    • - *
    • {@link Collection}: Considered empty if it has zero elements.
    • - *
    • {@link Map}: Considered empty if it has zero key-value mappings.
    • - *
    + *

    + * If all the values are {@code null} or the array is {@code null} or empty, then {@code true} is returned, otherwise {@code false} is returned. + *

    * *
    -     * ObjectUtils.isEmpty(null)             = true
    -     * ObjectUtils.isEmpty("")               = true
    -     * ObjectUtils.isEmpty("ab")             = false
    -     * ObjectUtils.isEmpty(new int[]{})      = true
    -     * ObjectUtils.isEmpty(new int[]{1,2,3}) = false
    -     * ObjectUtils.isEmpty(1234)             = false
    +     * ObjectUtils.allNull(*)                = false
    +     * ObjectUtils.allNull(*, null)          = false
    +     * ObjectUtils.allNull(null, *)          = false
    +     * ObjectUtils.allNull(null, null, *, *) = false
    +     * ObjectUtils.allNull(null)             = true
    +     * ObjectUtils.allNull(null, null)       = true
          * 
    * - * @param object the {@code Object} to test, may be {@code null} - * @return {@code true} if the object has a supported type and is empty or null, - * {@code false} otherwise - * @since 3.9 + * @param values The values to test, may be {@code null} or empty. + * @return {@code true} if all values in the array are {@code null}s, {@code false} if there is at least one non-null value in the array. + * @since 3.11 */ - public static boolean isEmpty(final Object object) { - if (object == null) { - return true; - } - if (object instanceof CharSequence) { - return ((CharSequence) object).length() == 0; - } - if (object.getClass().isArray()) { - return Array.getLength(object) == 0; - } - if (object instanceof Collection) { - return ((Collection) object).isEmpty(); - } - if (object instanceof Map) { - return ((Map) object).isEmpty(); - } - return false; + public static boolean allNull(final Object... values) { + return !anyNotNull(values); } /** - *

    Checks if an Object is not empty and not null.

    + * Tests if any value in the given array is not {@code null}. * - * The following types are supported: - *
      - *
    • {@link CharSequence}: Considered empty if its length is zero.
    • - *
    • {@code Array}: Considered empty if its length is zero.
    • - *
    • {@link Collection}: Considered empty if it has zero elements.
    • - *
    • {@link Map}: Considered empty if it has zero key-value mappings.
    • - *
    + *

    + * If all the values are {@code null} or the array is {@code null} or empty then {@code false} is returned. Otherwise {@code true} is returned. + *

    * *
    -     * ObjectUtils.isNotEmpty(null)             = false
    -     * ObjectUtils.isNotEmpty("")               = false
    -     * ObjectUtils.isNotEmpty("ab")             = true
    -     * ObjectUtils.isNotEmpty(new int[]{})      = false
    -     * ObjectUtils.isNotEmpty(new int[]{1,2,3}) = true
    -     * ObjectUtils.isNotEmpty(1234)             = true
    +     * ObjectUtils.anyNotNull(*)                = true
    +     * ObjectUtils.anyNotNull(*, null)          = true
    +     * ObjectUtils.anyNotNull(null, *)          = true
    +     * ObjectUtils.anyNotNull(null, null, *, *) = true
    +     * ObjectUtils.anyNotNull(null)             = false
    +     * ObjectUtils.anyNotNull(null, null)       = false
          * 
    * - * @param object the {@code Object} to test, may be {@code null} - * @return {@code true} if the object has an unsupported type or is not empty - * and not null, {@code false} otherwise - * @since 3.9 + * @param values The values to test, may be {@code null} or empty. + * @return {@code true} if there is at least one non-null value in the array, {@code false} if all values in the array are {@code null}s. If the array is + * {@code null} or empty {@code false} is also returned. + * @since 3.5 */ - public static boolean isNotEmpty(final Object object) { - return !isEmpty(object); + public static boolean anyNotNull(final Object... values) { + return firstNonNull(values) != null; } /** - *

    Returns a default value if the object passed is {@code null}.

    + * Tests if any value in the given array is {@code null}. + * + *

    + * If any of the values are {@code null} or the array is {@code null}, then {@code true} is returned, otherwise {@code false} is returned. + *

    * *
    -     * ObjectUtils.defaultIfNull(null, null)      = null
    -     * ObjectUtils.defaultIfNull(null, "")        = ""
    -     * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
    -     * ObjectUtils.defaultIfNull("abc", *)        = "abc"
    -     * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
    +     * ObjectUtils.anyNull(*)             = false
    +     * ObjectUtils.anyNull(*, *)          = false
    +     * ObjectUtils.anyNull(null)          = true
    +     * ObjectUtils.anyNull(null, null)    = true
    +     * ObjectUtils.anyNull(null, *)       = true
    +     * ObjectUtils.anyNull(*, null)       = true
    +     * ObjectUtils.anyNull(*, *, null, *) = true
          * 
    * - * @param the type of the object - * @param object the {@code Object} to test, may be {@code null} - * @param defaultValue the default value to return, may be {@code null} - * @return {@code object} if it is not {@code null}, defaultValue otherwise - * TODO Rename to getIfNull in 4.0 + * @param values The values to test, may be {@code null} or empty. + * @return {@code true} if there is at least one {@code null} value in the array, {@code false} if all the values are non-null or the array is empty. If the array is {@code null}, + * {@code true} is also returned. + * @since 3.11 */ - public static T defaultIfNull(final T object, final T defaultValue) { - return object != null ? object : defaultValue; + public static boolean anyNull(final Object... values) { + return !allNotNull(values); } /** - *

    Returns the first value in the array which is not {@code null}. - * If all the values are {@code null} or the array is {@code null} - * or empty then {@code null} is returned.

    + * Clones an object. * - *
    -     * ObjectUtils.firstNonNull(null, null)      = null
    -     * ObjectUtils.firstNonNull(null, "")        = ""
    -     * ObjectUtils.firstNonNull(null, null, "")  = ""
    -     * ObjectUtils.firstNonNull(null, "zz")      = "zz"
    -     * ObjectUtils.firstNonNull("abc", *)        = "abc"
    -     * ObjectUtils.firstNonNull(null, "xyz", *)  = "xyz"
    -     * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
    -     * ObjectUtils.firstNonNull()                = null
    -     * 
    - * - * @param the component type of the array - * @param values the values to test, may be {@code null} or empty - * @return the first value from {@code values} which is not {@code null}, - * or {@code null} if there are no non-null values + * @param The type of the object. + * @param obj The object to clone, null returns null. + * @return The clone if the object implements {@link Cloneable} otherwise {@code null}. + * @throws CloneFailedException if the object is cloneable and the clone operation fails. * @since 3.0 */ - @SafeVarargs - public static T firstNonNull(final T... values) { - if (values != null) { - for (final T val : values) { - if (val != null) { - return val; + public static T clone(final T obj) { + if (obj instanceof Cloneable) { + final Object result; + final Class objClass = obj.getClass(); + if (isArray(obj)) { + final Class componentType = objClass.getComponentType(); + if (componentType.isPrimitive()) { + int length = Array.getLength(obj); + result = Array.newInstance(componentType, length); + while (length-- > 0) { + Array.set(result, length, Array.get(obj, length)); + } + } else { + result = ((Object[]) obj).clone(); + } + } else { + try { + result = objClass.getMethod("clone").invoke(obj); + } catch (final ReflectiveOperationException e) { + throw new CloneFailedException("Exception cloning Cloneable type " + objClass.getName(), e); } } + return (T) result; } return null; } /** - *

    Executes the given suppliers in order and returns the first return - * value where a value other than {@code null} is returned. - * Once a non-{@code null} value is obtained, all following suppliers are - * not executed anymore. - * If all the return values are {@code null} or no suppliers are provided - * then {@code null} is returned.

    + * Clones an object if possible. * - *
    -     * ObjectUtils.firstNonNullLazy(null, () -> null) = null
    -     * ObjectUtils.firstNonNullLazy(() -> null, () -> "") = ""
    -     * ObjectUtils.firstNonNullLazy(() -> "", () -> throw new IllegalStateException()) = ""
    -     * ObjectUtils.firstNonNullLazy(() -> null, () -> "zz) = "zz"
    -     * ObjectUtils.firstNonNullLazy() = null
    -     * 
    + *

    + * This method is similar to {@link #clone(Object)}, but will return the provided instance as the return value instead of {@code null} if the instance is + * not cloneable. This is more convenient if the caller uses different implementations (e.g. of a service) and some of the implementations do not allow + * concurrent processing or have state. In such cases the implementation can simply provide a proper clone implementation and the caller's code does not + * have to change. + *

    * - * @param the type of the return values - * @param suppliers the suppliers returning the values to test. - * {@code null} values are ignored. - * Suppliers may return {@code null} or a value of type @{code T} - * @return the first return value from {@code suppliers} which is not {@code null}, - * or {@code null} if there are no non-null values - * @since 3.10 + * @param The type of the object. + * @param obj The object to clone, null returns null. + * @return The clone if the object implements {@link Cloneable} otherwise the object itself. + * @throws CloneFailedException if the object is cloneable and the clone operation fails. + * @since 3.0 */ - @SafeVarargs - public static T getFirstNonNull(final Supplier... suppliers) { - if (suppliers != null) { - for (final Supplier supplier : suppliers) { - if (supplier != null) { - T value = supplier.get(); - if (value != null) { - return value; - } - } - } - } - return null; + public static T cloneIfPossible(final T obj) { + final T clone = clone(obj); + return clone == null ? obj : clone; } /** + * Null safe comparison of Comparables. {@code null} is assumed to be less than a non-{@code null} value. *

    - * Returns the given {@code object} is it is non-null, otherwise returns the Supplier's {@link Supplier#get()} - * value. + * TODO Move to ComparableUtils. *

    * + * @param type of the values processed by this method. + * @param c1 The first comparable, may be null. + * @param c2 The second comparable, may be null. + * @return A negative value if c1 < c2, zero if c1 = c2 and a positive value if c1 > c2. + */ + public static > int compare(final T c1, final T c2) { + return compare(c1, c2, false); + } + + /** + * Null safe comparison of Comparables. *

    - * The caller responsible for thread-safety and exception handling of default value supplier. + * TODO Move to ComparableUtils. *

    * + * @param type of the values processed by this method. + * @param c1 The first comparable, may be null. + * @param c2 The second comparable, may be null. + * @param nullGreater if true {@code null} is considered greater than a non-{@code null} value or if false {@code null} is considered less than a + * Non-{@code null} value. + * @return A negative value if c1 < c2, zero if c1 = c2 and a positive value if c1 > c2. + * @see java.util.Comparator#compare(Object, Object) + */ + public static > int compare(final T c1, final T c2, final boolean nullGreater) { + if (c1 == c2) { + return 0; + } + if (c1 == null) { + return nullGreater ? 1 : -1; + } + if (c2 == null) { + return nullGreater ? -1 : 1; + } + return c1.compareTo(c2); + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * *
    -     * ObjectUtils.getIfNull(null, () -> null)     = null
    -     * ObjectUtils.getIfNull(null, null)              = null
    -     * ObjectUtils.getIfNull(null, () -> "")       = ""
    -     * ObjectUtils.getIfNull(null, () -> "zz")     = "zz"
    -     * ObjectUtils.getIfNull("abc", *)                = "abc"
    -     * ObjectUtils.getIfNull(Boolean.TRUE, *)         = Boolean.TRUE
    +     * public final static boolean MAGIC_FLAG = ObjectUtils.CONST(true);
          * 
    * - * @param the type of the object - * @param object the {@code Object} to test, may be {@code null} - * @param defaultSupplier the default value to return, may be {@code null} - * @return {@code object} if it is not {@code null}, {@code defaultValueSupplier.get()} otherwise - * @since 3.10 + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The boolean value to return. + * @return The boolean v, unchanged. + * @since 3.2 */ - public static T getIfNull(final T object, final Supplier defaultSupplier) { - return object != null ? object : defaultSupplier == null ? null : defaultSupplier.get(); + public static boolean CONST(final boolean v) { + return v; } /** - * Checks if any value in the given array is not {@code null}. + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., * - *

    - * If all the values are {@code null} or the array is {@code null} - * or empty then {@code false} is returned. Otherwise {@code true} is returned. - *

    + *
    +     * public final static byte MAGIC_BYTE = ObjectUtils.CONST((byte) 127);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The byte value to return. + * @return The byte v, unchanged. + * @since 3.2 + */ + public static byte CONST(final byte v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., * *
    -     * ObjectUtils.anyNotNull(*)                = true
    -     * ObjectUtils.anyNotNull(*, null)          = true
    -     * ObjectUtils.anyNotNull(null, *)          = true
    -     * ObjectUtils.anyNotNull(null, null, *, *) = true
    -     * ObjectUtils.anyNotNull(null)             = false
    -     * ObjectUtils.anyNotNull(null, null)       = false
    +     * public final static char MAGIC_CHAR = ObjectUtils.CONST('a');
          * 
    * - * @param values the values to test, may be {@code null} or empty - * @return {@code true} if there is at least one non-null value in the array, - * {@code false} if all values in the array are {@code null}s. - * If the array is {@code null} or empty {@code false} is also returned. - * @since 3.5 + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The char value to return. + * @return The char v, unchanged. + * @since 3.2 */ - public static boolean anyNotNull(final Object... values) { - return firstNonNull(values) != null; + public static char CONST(final char v) { + return v; } /** - * Checks if all values in the array are not {@code nulls}. + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., * - *

    - * If any value is {@code null} or the array is {@code null} then - * {@code false} is returned. If all elements in array are not - * {@code null} or the array is empty (contains no elements) {@code true} - * is returned. - *

    + *
    +     * public final static double MAGIC_DOUBLE = ObjectUtils.CONST(1.0);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The double value to return. + * @return The double v, unchanged. + * @since 3.2 + */ + public static double CONST(final double v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., * *
    -     * ObjectUtils.allNotNull(*)             = true
    -     * ObjectUtils.allNotNull(*, *)          = true
    -     * ObjectUtils.allNotNull(null)          = false
    -     * ObjectUtils.allNotNull(null, null)    = false
    -     * ObjectUtils.allNotNull(null, *)       = false
    -     * ObjectUtils.allNotNull(*, null)       = false
    -     * ObjectUtils.allNotNull(*, *, null, *) = false
    +     * public final static float MAGIC_FLOAT = ObjectUtils.CONST(1.0f);
          * 
    * - * @param values the values to test, may be {@code null} or empty - * @return {@code false} if there is at least one {@code null} value in the array or the array is {@code null}, - * {@code true} if all values in the array are not {@code null}s or array contains no elements. - * @since 3.5 + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The float value to return. + * @return The float v, unchanged. + * @since 3.2 */ - public static boolean allNotNull(final Object... values) { - if (values == null) { - return false; + public static float CONST(final float v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * + *
    +     * public final static int MAGIC_INT = ObjectUtils.CONST(123);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The int value to return. + * @return The int v, unchanged. + * @since 3.2 + */ + public static int CONST(final int v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * + *
    +     * public final static long MAGIC_LONG = ObjectUtils.CONST(123L);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The long value to return. + * @return The long v, unchanged. + * @since 3.2 + */ + public static long CONST(final long v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * + *
    +     * public final static short MAGIC_SHORT = ObjectUtils.CONST((short) 123);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The short value to return. + * @return The short v, unchanged. + * @since 3.2 + */ + public static short CONST(final short v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * + *
    +     * public final static String MAGIC_STRING = ObjectUtils.CONST("abc");
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param The Object type. + * @param v The genericized Object value to return (typically a String). + * @return The genericized Object v, unchanged (typically a String). + * @since 3.2 + */ + public static T CONST(final T v) { + return v; + } + + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * + *
    +     * public final static byte MAGIC_BYTE = ObjectUtils.CONST_BYTE(127);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The byte literal (as an int) value to return. + * @throws IllegalArgumentException if the value passed to v is larger than a byte, that is, smaller than -128 or larger than 127. + * @return The byte v, unchanged. + * @since 3.2 + */ + public static byte CONST_BYTE(final int v) { + if (v < Byte.MIN_VALUE || v > Byte.MAX_VALUE) { + throw new IllegalArgumentException("Supplied value must be a valid byte literal between -128 and 127: [" + v + "]"); } + return (byte) v; + } - for (final Object val : values) { - if (val == null) { - return false; - } + /** + * Returns the provided value unchanged. This can prevent javac from inlining a constant field, e.g., + * + *
    +     * public final static short MAGIC_SHORT = ObjectUtils.CONST_SHORT(127);
    +     * 
    + * + * This way any jars that refer to this field do not have to recompile themselves if the field's value changes at some future date. + * + * @param v The short literal (as an int) value to return. + * @throws IllegalArgumentException if the value passed to v is larger than a short, that is, smaller than -32768 or larger than 32767. + * @return The byte v, unchanged. + * @since 3.2 + */ + public static short CONST_SHORT(final int v) { + if (v < Short.MIN_VALUE || v > Short.MAX_VALUE) { + throw new IllegalArgumentException("Supplied value must be a valid byte literal between -32768 and 32767: [" + v + "]"); } + return (short) v; + } - return true; + /** + * Returns a default value if the object passed is {@code null}. + * + *
    +     * ObjectUtils.defaultIfNull(null, null)      = null
    +     * ObjectUtils.defaultIfNull(null, "")        = ""
    +     * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
    +     * ObjectUtils.defaultIfNull("abc", *)        = "abc"
    +     * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
    +     * 
    + * + * @param The type of the object. + * @param object The {@link Object} to test, may be {@code null}. + * @param defaultValue The default value to return, may be {@code null}. + * @return {@code object} if it is not {@code null}, defaultValue otherwise. + * @see #getIfNull(Object, Object) + * @see #getIfNull(Object, Supplier) + * @deprecated Use {@link #getIfNull(Object, Object)}. + */ + @Deprecated + public static T defaultIfNull(final T object, final T defaultValue) { + return getIfNull(object, defaultValue); } - // Null-safe equals/hashCode - //----------------------------------------------------------------------- /** - *

    Compares two objects for equality, where either one or both - * objects may be {@code null}.

    + * Compares two objects for equality, where either one or both + * objects may be {@code null}. * *
          * ObjectUtils.equals(null, null)                  = true
    @@ -360,107 +554,251 @@ public static boolean allNotNull(final Object... values) {
          * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
          * 
    * - * @param object1 the first object, may be {@code null} - * @param object2 the second object, may be {@code null} - * @return {@code true} if the values of both objects are the same - * @deprecated this method has been replaced by {@code java.util.Objects.equals(Object, Object)} in Java 7 and will + * @param object1 The first object, may be {@code null}. + * @param object2 The second object, may be {@code null}. + * @return {@code true} if the values of both objects are the same. + * @deprecated Replaced by {@code java.util.Objects.equals(Object, Object)} in Java 7 and will * be removed from future releases. */ @Deprecated public static boolean equals(final Object object1, final Object object2) { - if (object1 == object2) { - return true; - } - if (object1 == null || object2 == null) { - return false; - } - return object1.equals(object2); + return Objects.equals(object1, object2); } /** - *

    Compares two objects for inequality, where either one or both - * objects may be {@code null}.

    + * Returns the first value in the array which is not {@code null}. + * If all the values are {@code null} or the array is {@code null} + * or empty then {@code null} is returned. * *
    -     * ObjectUtils.notEqual(null, null)                  = false
    -     * ObjectUtils.notEqual(null, "")                    = true
    -     * ObjectUtils.notEqual("", null)                    = true
    -     * ObjectUtils.notEqual("", "")                      = false
    -     * ObjectUtils.notEqual(Boolean.TRUE, null)          = true
    -     * ObjectUtils.notEqual(Boolean.TRUE, "true")        = true
    -     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE)  = false
    -     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
    +     * ObjectUtils.firstNonNull(null, null)      = null
    +     * ObjectUtils.firstNonNull(null, "")        = ""
    +     * ObjectUtils.firstNonNull(null, null, "")  = ""
    +     * ObjectUtils.firstNonNull(null, "zz")      = "zz"
    +     * ObjectUtils.firstNonNull("abc", *)        = "abc"
    +     * ObjectUtils.firstNonNull(null, "xyz", *)  = "xyz"
    +     * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
    +     * ObjectUtils.firstNonNull()                = null
          * 
    * - * @param object1 the first object, may be {@code null} - * @param object2 the second object, may be {@code null} - * @return {@code false} if the values of both objects are the same + * @param The component type of the array. + * @param values The values to test, may be {@code null} or empty. + * @return The first value from {@code values} which is not {@code null}, + * or {@code null} if there are no non-null values. + * @since 3.0 */ - public static boolean notEqual(final Object object1, final Object object2) { - return !equals(object1, object2); + @SafeVarargs + public static T firstNonNull(final T... values) { + return Streams.of(values).filter(Objects::nonNull).findFirst().orElse(null); + } + + /** + * Delegates to {@link Object#getClass()} using generics. + * + * @param The argument type or null. + * @param object The argument. + * @return The argument's Class or null. + * @since 3.13.0 + */ + @SuppressWarnings("unchecked") + public static Class getClass(final T object) { + return object == null ? null : (Class) object.getClass(); + } + + /** + * Executes the given suppliers in order and returns the first return value where a value other than {@code null} is returned. Once a non-{@code null} value + * is obtained, all following suppliers are not executed anymore. If all the return values are {@code null} or no suppliers are provided then {@code null} + * is returned. + * + *
    {@code
    +     * ObjectUtils.firstNonNullLazy(null, () -> null)                                  = null
    +     * ObjectUtils.firstNonNullLazy(() -> null, () -> "")                              = ""
    +     * ObjectUtils.firstNonNullLazy(() -> "", () -> throw new IllegalStateException()) = ""
    +     * ObjectUtils.firstNonNullLazy(() -> null, () -> "zz)                             = "zz"
    +     * ObjectUtils.firstNonNullLazy()                                                  = null
    +     * }
    + *

    + * See also {@link Consumers#accept(Consumer, Object)} and {@link Suppliers#get(Supplier)}. + *

    + * + * @param the type of the return values. + * @param suppliers The suppliers returning the values to test. {@code null} values are ignored. Suppliers may return {@code null} or a value of type + * {@code T}. + * @return The first return value from {@code suppliers} which is not {@code null}, or {@code null} if there are no non-null values. + * @see Consumers#accept(Consumer, Object) + * @see Suppliers#get(Supplier) + * @since 3.10 + */ + @SafeVarargs + public static T getFirstNonNull(final Supplier... suppliers) { + return Streams.of(suppliers).filter(Objects::nonNull).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null); } /** - *

    Gets the hash code of an object returning zero when the - * object is {@code null}.

    + * Returns the given {@code object} is it is non-null, otherwise returns the Supplier's {@link Supplier#get()} + * value. + * + *

    + * The caller responsible for thread-safety and exception handling of default value supplier. + *

    + * + *
    {@code
    +     * ObjectUtils.getIfNull(null, () -> null)     = null
    +     * ObjectUtils.getIfNull(null, null)           = null
    +     * ObjectUtils.getIfNull(null, () -> "")       = ""
    +     * ObjectUtils.getIfNull(null, () -> "zz")     = "zz"
    +     * ObjectUtils.getIfNull("abc", *)             = "abc"
    +     * ObjectUtils.getIfNull(Boolean.TRUE, *)      = Boolean.TRUE
    +     * }
    + *

    + * See also {@link Consumers#accept(Consumer, Object)} and {@link Suppliers#get(Supplier)}. + *

    + * + * @param The type of the object. + * @param object The {@link Object} to test, may be {@code null}. + * @param defaultSupplier The default value to return, may be {@code null}. + * @return {@code object} if it is not {@code null}, {@code defaultValueSupplier.get()} otherwise. + * @see #getIfNull(Object, Object) + * @see Consumers#accept(Consumer, Object) + * @see Suppliers#get(Supplier) + * @since 3.10 + */ + public static T getIfNull(final T object, final Supplier defaultSupplier) { + return object != null ? object : Suppliers.get(defaultSupplier); + } + + /** + * Returns a default value if the object passed is {@code null}. + * + *
    +     * ObjectUtils.getIfNull(null, null)      = null
    +     * ObjectUtils.getIfNull(null, "")        = ""
    +     * ObjectUtils.getIfNull(null, "zz")      = "zz"
    +     * ObjectUtils.getIfNull("abc", *)        = "abc"
    +     * ObjectUtils.getIfNull(Boolean.TRUE, *) = Boolean.TRUE
    +     * 
    + *

    + * See also {@link Consumers#accept(Consumer, Object)} and {@link Suppliers#get(Supplier)}. + *

    + * + * @param The type of the object. + * @param object The {@link Object} to test, may be {@code null}. + * @param defaultValue The default value to return, may be {@code null}. + * @return {@code object} if it is not {@code null}, defaultValue otherwise. + * @see #getIfNull(Object, Supplier) + * @see Consumers#accept(Consumer, Object) + * @see Suppliers#get(Supplier) + * @since 3.18.0 + */ + public static T getIfNull(final T object, final T defaultValue) { + return object != null ? object : defaultValue; + } + + /** + * Gets the hash code of an object returning zero when the object is {@code null}. * *
          * ObjectUtils.hashCode(null)   = 0
          * ObjectUtils.hashCode(obj)    = obj.hashCode()
          * 
    * - * @param obj the object to obtain the hash code of, may be {@code null} - * @return the hash code of the object, or zero if null + * @param obj The object to obtain the hash code of, may be {@code null}. + * @return The hash code of the object, or zero if null. * @since 2.1 - * @deprecated this method has been replaced by {@code java.util.Objects.hashCode(Object)} in Java 7 and will be - * removed in future releases + * @deprecated Replaced by {@code java.util.Objects.hashCode(Object)} in Java 7 and will be removed in future releases. */ @Deprecated public static int hashCode(final Object obj) { - // hashCode(Object) retained for performance, as hash code is often critical - return obj == null ? 0 : obj.hashCode(); + // hashCode(Object) for performance vs. hashCodeMulti(Object[]), as hash code is often critical + return Objects.hashCode(obj); + } + + /** + * Returns the hexadecimal hash code for the given object per {@link Objects#hashCode(Object)}. + *

    + * Short hand for {@code Integer.toHexString(Objects.hashCode(object))}. + *

    + * + * @param object object for which the hashCode is to be calculated. + * @return Hash code in hexadecimal format. + * @since 3.13.0 + */ + public static String hashCodeHex(final Object object) { + return Integer.toHexString(Objects.hashCode(object)); } /** - *

    Gets the hash code for multiple objects.

    + * Gets the hash code for multiple objects. * - *

    This allows a hash code to be rapidly calculated for a number of objects. - * The hash code for a single object is the not same as {@link #hashCode(Object)}. - * The hash code for multiple objects is the same as that calculated by an - * {@code ArrayList} containing the specified objects.

    + *

    + * This allows a hash code to be rapidly calculated for a number of objects. The hash code for a single object is the not same as + * {@link #hashCode(Object)}. The hash code for multiple objects is the same as that calculated by an {@link ArrayList} containing the specified objects. + *

    * *
          * ObjectUtils.hashCodeMulti()                 = 1
          * ObjectUtils.hashCodeMulti((Object[]) null)  = 1
          * ObjectUtils.hashCodeMulti(a)                = 31 + a.hashCode()
    -     * ObjectUtils.hashCodeMulti(a,b)              = (31 + a.hashCode()) * 31 + b.hashCode()
    -     * ObjectUtils.hashCodeMulti(a,b,c)            = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode()
    +     * ObjectUtils.hashCodeMulti(a, b)             = (31 + a.hashCode()) * 31 + b.hashCode()
    +     * ObjectUtils.hashCodeMulti(a, b, c)          = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode()
          * 
    * - * @param objects the objects to obtain the hash code of, may be {@code null} - * @return the hash code of the objects, or zero if null + * @param objects The objects to obtain the hash code of, may be {@code null}. + * @return The hash code of the objects, or zero if null. * @since 3.0 - * @deprecated this method has been replaced by {@code java.util.Objects.hash(Object...)} in Java 7 and will be - * removed in future releases. + * @deprecated Replaced by {@code java.util.Objects.hash(Object...)} in Java 7 and will be removed in future releases. */ @Deprecated public static int hashCodeMulti(final Object... objects) { int hash = 1; if (objects != null) { for (final Object object : objects) { - final int tmpHash = hashCode(object); + final int tmpHash = Objects.hashCode(object); hash = hash * 31 + tmpHash; } } return hash; } - // Identity ToString - //----------------------------------------------------------------------- /** - *

    Gets the toString that would be produced by {@code Object} + * Returns the hexadecimal hash code for the given object per {@link System#identityHashCode(Object)}. + *

    + * Short hand for {@code Integer.toHexString(System.identityHashCode(object))}. + *

    + * + * @param object object for which the hashCode is to be calculated. + * @return Hash code in hexadecimal format. + * @since 3.13.0 + */ + public static String identityHashCodeHex(final Object object) { + return Integer.toHexString(System.identityHashCode(object)); + } + + /** + * Appends the toString that would be produced by {@link Object} * if a class did not override toString itself. {@code null} - * will return {@code null}.

    + * will throw a NullPointerException for either of the two parameters. + * + *
    +     * ObjectUtils.identityToString(appendable, "")            = appendable.append("java.lang.String@1e23")
    +     * ObjectUtils.identityToString(appendable, Boolean.TRUE)  = appendable.append("java.lang.Boolean@7fa")
    +     * ObjectUtils.identityToString(appendable, Boolean.TRUE)  = appendable.append("java.lang.Boolean@7fa")
    +     * 
    + * + * @param appendable The appendable to append to. + * @param object The object to create a toString for. + * @throws IOException Thrown if an I/O error occurs. + * @since 3.2 + */ + public static void identityToString(final Appendable appendable, final Object object) throws IOException { + Objects.requireNonNull(object, "object"); + appendable.append(object.getClass().getName()) + .append(AT_SIGN) + .append(identityHashCodeHex(object)); + } + + /** + * Gets the toString that would be produced by {@link Object} if a class did not override toString itself. {@code null} will return {@code null}. * *
          * ObjectUtils.identityToString(null)         = null
    @@ -468,71 +806,46 @@ public static int hashCodeMulti(final Object... objects) {
          * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
          * 
    * - * @param object the object to create a toString for, may be - * {@code null} - * @return the default toString text, or {@code null} if - * {@code null} passed in + * @param object The object to create a toString for, may be {@code null}. + * @return The default toString text, or {@code null} if {@code null} passed in. */ public static String identityToString(final Object object) { if (object == null) { return null; } final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); + final String hexString = identityHashCodeHex(object); final StringBuilder builder = new StringBuilder(name.length() + 1 + hexString.length()); // @formatter:off builder.append(name) .append(AT_SIGN) .append(hexString); - // @formatter:off + // @formatter:on return builder.toString(); } /** - *

    Appends the toString that would be produced by {@code Object} + * Appends the toString that would be produced by {@link Object} * if a class did not override toString itself. {@code null} - * will throw a NullPointerException for either of the two parameters.

    + * will throw a NullPointerException for either of the two parameters. * *
    -     * ObjectUtils.identityToString(appendable, "")            = appendable.append("java.lang.String@1e23"
    -     * ObjectUtils.identityToString(appendable, Boolean.TRUE)  = appendable.append("java.lang.Boolean@7fa"
    -     * ObjectUtils.identityToString(appendable, Boolean.TRUE)  = appendable.append("java.lang.Boolean@7fa")
    -     * 
    - * - * @param appendable the appendable to append to - * @param object the object to create a toString for - * @throws IOException if an I/O error occurs - * @since 3.2 - */ - public static void identityToString(final Appendable appendable, final Object object) throws IOException { - Validate.notNull(object, "Cannot get the toString of a null object"); - appendable.append(object.getClass().getName()) - .append(AT_SIGN) - .append(Integer.toHexString(System.identityHashCode(object))); - } - - /** - *

    Appends the toString that would be produced by {@code Object} - * if a class did not override toString itself. {@code null} - * will throw a NullPointerException for either of the two parameters.

    - * - *
    -     * ObjectUtils.identityToString(builder, "")            = builder.append("java.lang.String@1e23"
    -     * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa"
    +     * ObjectUtils.identityToString(builder, "")            = builder.append("java.lang.String@1e23")
    +     * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa")
          * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa")
          * 
    * - * @param builder the builder to append to - * @param object the object to create a toString for + * @param builder The builder to append to. + * @param object The object to create a toString for. * @since 3.2 * @deprecated as of 3.6, because StrBuilder was moved to commons-text, - * use one of the other {@code identityToString} methods instead + * use one of the other {@code identityToString} methods instead. */ @Deprecated public static void identityToString(final StrBuilder builder, final Object object) { - Validate.notNull(object, "Cannot get the toString of a null object"); + Objects.requireNonNull(object, "object"); final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); + final String hexString = identityHashCodeHex(object); builder.ensureCapacity(builder.length() + name.length() + 1 + hexString.length()); builder.append(name) .append(AT_SIGN) @@ -540,24 +853,24 @@ public static void identityToString(final StrBuilder builder, final Object objec } /** - *

    Appends the toString that would be produced by {@code Object} + * Appends the toString that would be produced by {@link Object} * if a class did not override toString itself. {@code null} - * will throw a NullPointerException for either of the two parameters.

    + * will throw a NullPointerException for either of the two parameters. * *
    -     * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23"
    -     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa"
    +     * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23")
    +     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
          * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
          * 
    * - * @param buffer the buffer to append to - * @param object the object to create a toString for + * @param buffer The buffer to append to. + * @param object The object to create a toString for. * @since 2.4 */ public static void identityToString(final StringBuffer buffer, final Object object) { - Validate.notNull(object, "Cannot get the toString of a null object"); + Objects.requireNonNull(object, "object"); final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); + final String hexString = identityHashCodeHex(object); buffer.ensureCapacity(buffer.length() + name.length() + 1 + hexString.length()); buffer.append(name) .append(AT_SIGN) @@ -565,123 +878,154 @@ public static void identityToString(final StringBuffer buffer, final Object obje } /** - *

    Appends the toString that would be produced by {@code Object} + * Appends the toString that would be produced by {@link Object} * if a class did not override toString itself. {@code null} - * will throw a NullPointerException for either of the two parameters.

    + * will throw a NullPointerException for either of the two parameters. * *
    -     * ObjectUtils.identityToString(builder, "")            = builder.append("java.lang.String@1e23"
    -     * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa"
    +     * ObjectUtils.identityToString(builder, "")            = builder.append("java.lang.String@1e23")
    +     * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa")
          * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa")
          * 
    * - * @param builder the builder to append to - * @param object the object to create a toString for + * @param builder The builder to append to. + * @param object The object to create a toString for. * @since 3.2 */ public static void identityToString(final StringBuilder builder, final Object object) { - Validate.notNull(object, "Cannot get the toString of a null object"); + Objects.requireNonNull(object, "object"); final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); + final String hexString = identityHashCodeHex(object); builder.ensureCapacity(builder.length() + name.length() + 1 + hexString.length()); builder.append(name) .append(AT_SIGN) .append(hexString); } - // ToString - //----------------------------------------------------------------------- /** - *

    Gets the {@code toString} of an {@code Object} returning - * an empty string ("") if {@code null} input.

    + * Tests whether the given object is an Object array or a primitive array in a null-safe manner. + * + *

    + * A {@code null} {@code object} Object will return {@code false}. + *

    * *
    -     * ObjectUtils.toString(null)         = ""
    -     * ObjectUtils.toString("")           = ""
    -     * ObjectUtils.toString("bat")        = "bat"
    -     * ObjectUtils.toString(Boolean.TRUE) = "true"
    +     * ObjectUtils.isArray(null)             = false
    +     * ObjectUtils.isArray("")               = false
    +     * ObjectUtils.isArray("ab")             = false
    +     * ObjectUtils.isArray(new int[]{})      = true
    +     * ObjectUtils.isArray(new int[]{1,2,3}) = true
    +     * ObjectUtils.isArray(1234)             = false
          * 
    * - * @see StringUtils#defaultString(String) - * @see String#valueOf(Object) - * @param obj the Object to {@code toString}, may be null - * @return the passed in Object's toString, or {@code ""} if {@code null} input - * @since 2.0 - * @deprecated this method has been replaced by {@code java.util.Objects.toString(Object)} in Java 7 and will be - * removed in future releases. Note however that said method will return "null" for null references, while this - * method returns an empty String. To preserve behavior use {@code java.util.Objects.toString(myObject, "")} + * @param object The object to check, may be {@code null}. + * @return {@code true} if the object is an {@code array}, {@code false} otherwise. + * @since 3.13.0 */ - @Deprecated - public static String toString(final Object obj) { - return obj == null ? StringUtils.EMPTY : obj.toString(); + public static boolean isArray(final Object object) { + return object != null && object.getClass().isArray(); } /** - *

    Gets the {@code toString} of an {@code Object} returning - * a specified text if {@code null} input.

    + * Tests if an Object is empty or null. + *

    + * The following types are supported: + *

    + *
      + *
    • {@link CharSequence}: Considered empty if its length is zero.
    • + *
    • {@link Array}: Considered empty if its length is zero.
    • + *
    • {@link Collection}: Considered empty if it has zero elements.
    • + *
    • {@link Map}: Considered empty if it has zero key-value mappings.
    • + *
    • {@link Optional}: Considered empty if {@link Optional#isPresent} returns false, regardless of the "emptiness" of the contents.
    • + *
    * *
    -     * ObjectUtils.toString(null, null)           = null
    -     * ObjectUtils.toString(null, "null")         = "null"
    -     * ObjectUtils.toString("", "null")           = ""
    -     * ObjectUtils.toString("bat", "null")        = "bat"
    -     * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
    +     * ObjectUtils.isEmpty(null)             = true
    +     * ObjectUtils.isEmpty("")               = true
    +     * ObjectUtils.isEmpty("ab")             = false
    +     * ObjectUtils.isEmpty(new int[]{})      = true
    +     * ObjectUtils.isEmpty(new int[]{1,2,3}) = false
    +     * ObjectUtils.isEmpty(1234)             = false
    +     * ObjectUtils.isEmpty(1234)             = false
    +     * ObjectUtils.isEmpty(Optional.of(""))  = false
    +     * ObjectUtils.isEmpty(Optional.empty()) = true
          * 
    * - * @see StringUtils#defaultString(String,String) - * @see String#valueOf(Object) - * @param obj the Object to {@code toString}, may be null - * @param nullStr the String to return if {@code null} input, may be null - * @return the passed in Object's toString, or {@code nullStr} if {@code null} input - * @since 2.0 - * @deprecated this method has been replaced by {@code java.util.Objects.toString(Object, String)} in Java 7 and - * will be removed in future releases. + * @param object The {@link Object} to test, may be {@code null}. + * @return {@code true} if the object has a supported type and is empty or null, {@code false} otherwise. + * @since 3.9 */ - @Deprecated - public static String toString(final Object obj, final String nullStr) { - return obj == null ? nullStr : obj.toString(); + public static boolean isEmpty(final Object object) { + if (object == null) { + return true; + } + if (object instanceof CharSequence) { + return ((CharSequence) object).length() == 0; + } + if (isArray(object)) { + return Array.getLength(object) == 0; + } + if (object instanceof Collection) { + return ((Collection) object).isEmpty(); + } + if (object instanceof Map) { + return ((Map) object).isEmpty(); + } + if (object instanceof Optional) { + // TODO Java 11 Use Optional#isEmpty() + return !((Optional) object).isPresent(); + } + return false; } - // Comparable - //----------------------------------------------------------------------- /** - *

    Null safe comparison of Comparables.

    + * Tests if an Object is not empty and not null. + *

    + * The following types are supported: + *

    + *
      + *
    • {@link CharSequence}: Considered empty if its length is zero.
    • + *
    • {@link Array}: Considered empty if its length is zero.
    • + *
    • {@link Collection}: Considered empty if it has zero elements.
    • + *
    • {@link Map}: Considered empty if it has zero key-value mappings.
    • + *
    • {@link Optional}: Considered empty if {@link Optional#isPresent} returns false, regardless of the "emptiness" of the contents.
    • + *
    * - * @param type of the values processed by this method - * @param values the set of comparable values, may be null - * @return - *
      - *
    • If any objects are non-null and unequal, the lesser object. - *
    • If all objects are non-null and equal, the first. - *
    • If any of the comparables are null, the lesser of the non-null objects. - *
    • If all the comparables are null, null is returned. - *
    + *
    +     * ObjectUtils.isNotEmpty(null)             = false
    +     * ObjectUtils.isNotEmpty("")               = false
    +     * ObjectUtils.isNotEmpty("ab")             = true
    +     * ObjectUtils.isNotEmpty(new int[]{})      = false
    +     * ObjectUtils.isNotEmpty(new int[]{1,2,3}) = true
    +     * ObjectUtils.isNotEmpty(1234)             = true
    +     * ObjectUtils.isNotEmpty(Optional.of(""))  = true
    +     * ObjectUtils.isNotEmpty(Optional.empty()) = false
    +     * 
    + * + * @param object The {@link Object} to test, may be {@code null}. + * @return {@code true} if the object has an unsupported type or is not empty. + * and not null, {@code false} otherwise. + * @since 3.9 */ - @SafeVarargs - public static > T min(final T... values) { - T result = null; - if (values != null) { - for (final T value : values) { - if (compare(value, result, true) < 0) { - result = value; - } - } - } - return result; + public static boolean isNotEmpty(final Object object) { + return !isEmpty(object); } /** - *

    Null safe comparison of Comparables.

    + * Null safe comparison of Comparables. + *

    + * TODO Move to ComparableUtils. + *

    * - * @param type of the values processed by this method - * @param values the set of comparable values, may be null + * @param type of the values processed by this method. + * @param values The set of comparable values, may be null. * @return - *
      - *
    • If any objects are non-null and unequal, the greater object. - *
    • If all objects are non-null and equal, the first. - *
    • If any of the comparables are null, the greater of the non-null objects. - *
    • If all the comparables are null, null is returned. - *
    + *
      + *
    • If any objects are non-null and unequal, the greater object.
    • + *
    • If all objects are non-null and equal, the first.
    • + *
    • If any of the comparables are null, the greater of the non-null objects.
    • + *
    • If all the comparables are null, null is returned.
    • + *
    */ @SafeVarargs public static > T max(final T... values) { @@ -697,96 +1041,81 @@ public static > T max(final T... values) { } /** - *

    Null safe comparison of Comparables. - * {@code null} is assumed to be less than a non-{@code null} value.

    - * - * @param type of the values processed by this method - * @param c1 the first comparable, may be null - * @param c2 the second comparable, may be null - * @return a negative value if c1 < c2, zero if c1 = c2 - * and a positive value if c1 > c2 - */ - public static > int compare(final T c1, final T c2) { - return compare(c1, c2, false); - } - - /** - *

    Null safe comparison of Comparables.

    - * - * @param type of the values processed by this method - * @param c1 the first comparable, may be null - * @param c2 the second comparable, may be null - * @param nullGreater if true {@code null} is considered greater - * than a non-{@code null} value or if false {@code null} is - * considered less than a Non-{@code null} value - * @return a negative value if c1 < c2, zero if c1 = c2 - * and a positive value if c1 > c2 - * @see java.util.Comparator#compare(Object, Object) + * Finds the "best guess" middle value among comparables. If there is an even + * number of total values, the lower of the two middle values will be returned. + * + * @param type of values processed by this method. + * @param comparator to use for comparisons. + * @param items to compare. + * @return T at middle position. + * @throws NullPointerException if items or comparator is {@code null}. + * @throws IllegalArgumentException if items is empty or contains {@code null} values. + * @since 3.0.1 */ - public static > int compare(final T c1, final T c2, final boolean nullGreater) { - if (c1 == c2) { - return 0; - } else if (c1 == null) { - return nullGreater ? 1 : -1; - } else if (c2 == null) { - return nullGreater ? -1 : 1; - } - return c1.compareTo(c2); + @SafeVarargs + public static T median(final Comparator comparator, final T... items) { + Validate.notEmpty(items, "null/empty items"); + Validate.noNullElements(items); + Objects.requireNonNull(comparator, "comparator"); + final T[] sorted = items.clone(); + Arrays.sort(sorted, comparator); + return sorted[(sorted.length - 1) / 2]; } /** - * Find the "best guess" middle value among comparables. If there is an even - * number of total values, the lower of the two middle values will be returned. - * @param type of values processed by this method - * @param items to compare - * @return T at middle position - * @throws NullPointerException if items is {@code null} - * @throws IllegalArgumentException if items is empty or contains {@code null} values + * Finds the "best guess" middle value among comparables. If there is an even number of total values, the lower of the two middle values will be returned. + * + * @param type of values processed by this method. + * @param items to compare. + * @return T at middle position. + * @throws NullPointerException if items is {@code null}. + * @throws IllegalArgumentException if items is empty or contains {@code null} values. * @since 3.0.1 */ @SafeVarargs public static > T median(final T... items) { Validate.notEmpty(items); Validate.noNullElements(items); - final TreeSet sort = new TreeSet<>(); - Collections.addAll(sort, items); - @SuppressWarnings("unchecked") //we know all items added were T instances - final T result = (T) sort.toArray()[(sort.size() - 1) / 2]; - return result; + final T[] sorted = items.clone(); + Arrays.sort(sorted); + return sorted[(sorted.length - 1) / 2]; } /** - * Find the "best guess" middle value among comparables. If there is an even - * number of total values, the lower of the two middle values will be returned. - * @param type of values processed by this method - * @param comparator to use for comparisons - * @param items to compare - * @return T at middle position - * @throws NullPointerException if items or comparator is {@code null} - * @throws IllegalArgumentException if items is empty or contains {@code null} values - * @since 3.0.1 + * Null safe comparison of Comparables. + *

    + * TODO Move to ComparableUtils. + *

    + * + * @param type of the values processed by this method + * @param values The set of comparable values, may be null + * @return + *
      + *
    • If any objects are non-null and unequal, the lesser object.
    • + *
    • If all objects are non-null and equal, the first.
    • + *
    • If any of the comparables are null, the lesser of the non-null objects.
    • + *
    • If all the comparables are null, null is returned.
    • + *
    */ @SafeVarargs - public static T median(final Comparator comparator, final T... items) { - Validate.notEmpty(items, "null/empty items"); - Validate.noNullElements(items); - Validate.notNull(comparator, "null comparator"); - final TreeSet sort = new TreeSet<>(comparator); - Collections.addAll(sort, items); - @SuppressWarnings("unchecked") //we know all items added were T instances - final - T result = (T) sort.toArray()[(sort.size() - 1) / 2]; + public static > T min(final T... values) { + T result = null; + if (values != null) { + for (final T value : values) { + if (compare(value, result, true) < 0) { + result = value; + } + } + } return result; } - // Mode - //----------------------------------------------------------------------- /** - * Find the most frequently occurring item. + * Finds the most frequently occurring item. * - * @param type of values processed by this method - * @param items to check - * @return most populous T, {@code null} if non-unique or no items supplied + * @param type of values processed by this method. + * @param items to check. + * @return most populous T, {@code null} if non-unique or no items supplied. * @since 3.0.1 */ @SafeVarargs @@ -794,12 +1123,7 @@ public static T mode(final T... items) { if (ArrayUtils.isNotEmpty(items)) { final HashMap occurrences = new HashMap<>(items.length); for (final T t : items) { - final MutableInt count = occurrences.get(t); - if (count == null) { - occurrences.put(t, new MutableInt(1)); - } else { - count.increment(); - } + ArrayUtils.increment(occurrences, t); } T result = null; int max = 0; @@ -817,381 +1141,208 @@ public static T mode(final T... items) { return null; } - // cloning - //----------------------------------------------------------------------- - /** - *

    Clone an object.

    - * - * @param the type of the object - * @param obj the object to clone, null returns null - * @return the clone if the object implements {@link Cloneable} otherwise {@code null} - * @throws CloneFailedException if the object is cloneable and the clone operation fails - * @since 3.0 - */ - public static T clone(final T obj) { - if (obj instanceof Cloneable) { - final Object result; - if (obj.getClass().isArray()) { - final Class componentType = obj.getClass().getComponentType(); - if (componentType.isPrimitive()) { - int length = Array.getLength(obj); - result = Array.newInstance(componentType, length); - while (length-- > 0) { - Array.set(result, length, Array.get(obj, length)); - } - } else { - result = ((Object[]) obj).clone(); - } - } else { - try { - final Method clone = obj.getClass().getMethod("clone"); - result = clone.invoke(obj); - } catch (final NoSuchMethodException e) { - throw new CloneFailedException("Cloneable type " - + obj.getClass().getName() - + " has no clone method", e); - } catch (final IllegalAccessException e) { - throw new CloneFailedException("Cannot clone Cloneable type " - + obj.getClass().getName(), e); - } catch (final InvocationTargetException e) { - throw new CloneFailedException("Exception cloning Cloneable type " - + obj.getClass().getName(), e.getCause()); - } - } - @SuppressWarnings("unchecked") // OK because input is of type T - final T checked = (T) result; - return checked; - } - - return null; - } - - /** - *

    Clone an object if possible.

    - * - *

    This method is similar to {@link #clone(Object)}, but will return the provided - * instance as the return value instead of {@code null} if the instance - * is not cloneable. This is more convenient if the caller uses different - * implementations (e.g. of a service) and some of the implementations do not allow concurrent - * processing or have state. In such cases the implementation can simply provide a proper - * clone implementation and the caller's code does not have to change.

    - * - * @param the type of the object - * @param obj the object to clone, null returns null - * @return the clone if the object implements {@link Cloneable} otherwise the object itself - * @throws CloneFailedException if the object is cloneable and the clone operation fails - * @since 3.0 - */ - public static T cloneIfPossible(final T obj) { - final T clone = clone(obj); - return clone == null ? obj : clone; - } - - // Null - //----------------------------------------------------------------------- - /** - *

    Class used as a null placeholder where {@code null} - * has another meaning.

    - * - *

    For example, in a {@code HashMap} the - * {@link java.util.HashMap#get(java.lang.Object)} method returns - * {@code null} if the {@code Map} contains {@code null} or if there is - * no matching key. The {@code Null} placeholder can be used to distinguish - * between these two cases.

    - * - *

    Another example is {@code Hashtable}, where {@code null} - * cannot be stored.

    - */ - public static class Null implements Serializable { - /** - * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0 - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 7092611880189329093L; - - /** - * Restricted constructor - singleton. - */ - Null() { - super(); - } - - /** - *

    Ensure singleton.

    - * - * @return the singleton value - */ - private Object readResolve() { - return NULL; - } - } - - - // Constants (LANG-816): - /* - These methods ensure constants are not inlined by javac. - For example, typically a developer might declare a constant like so: - - public final static int MAGIC_NUMBER = 5; - - Should a different jar file refer to this, and the MAGIC_NUMBER - is changed a later date (e.g., MAGIC_NUMBER = 6), the different jar - file will need to recompile itself. This is because javac - typically inlines the primitive or String constant directly into - the bytecode, and removes the reference to the MAGIC_NUMBER field. - - To help the other jar (so that it does not need to recompile - when constants are changed) the original developer can declare - their constant using one of the CONST() utility methods, instead: - - public final static int MAGIC_NUMBER = CONST(5); - */ - - /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., + * Compares two objects for inequality, where either one or both + * objects may be {@code null}. * *
    -     *     public final static boolean MAGIC_FLAG = ObjectUtils.CONST(true);
    +     * ObjectUtils.notEqual(null, null)                  = false
    +     * ObjectUtils.notEqual(null, "")                    = true
    +     * ObjectUtils.notEqual("", null)                    = true
    +     * ObjectUtils.notEqual("", "")                      = false
    +     * ObjectUtils.notEqual(Boolean.TRUE, null)          = true
    +     * ObjectUtils.notEqual(Boolean.TRUE, "true")        = true
    +     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE)  = false
    +     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
          * 
    * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the boolean value to return - * @return the boolean v, unchanged - * @since 3.2 + * @param object1 The first object, may be {@code null}. + * @param object2 The second object, may be {@code null}. + * @return {@code false} if the values of both objects are the same. */ - public static boolean CONST(final boolean v) { - return v; + public static boolean notEqual(final Object object1, final Object object2) { + return !Objects.equals(object1, object2); } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., + * Checks that the specified object reference is not {@code null} or empty per {@link #isEmpty(Object)}. Use this + * method for validation, for example: * *
    -     *     public final static byte MAGIC_BYTE = ObjectUtils.CONST((byte) 127);
    +     * public Foo(Bar bar) {
    +     *     this.bar = Objects.requireNonEmpty(bar);
    +     * }
          * 
    * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the byte value to return - * @return the byte v, unchanged - * @since 3.2 + * @param The type of the reference. + * @param obj The object reference to check for nullity. + * @return {@code obj} if not {@code null}. + * @throws NullPointerException if {@code obj} is {@code null}. + * @throws IllegalArgumentException if {@code obj} is empty per {@link #isEmpty(Object)}. + * @see #isEmpty(Object) + * @since 3.12.0 */ - public static byte CONST(final byte v) { - return v; + public static T requireNonEmpty(final T obj) { + return requireNonEmpty(obj, "object"); } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., + * Checks that the specified object reference is not {@code null} or empty per {@link #isEmpty(Object)}. Use this + * method for validation, for example: * *
    -     *     public final static byte MAGIC_BYTE = ObjectUtils.CONST_BYTE(127);
    +     * public Foo(Bar bar) {
    +     *     this.bar = Objects.requireNonEmpty(bar, "bar");
    +     * }
          * 
    * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the byte literal (as an int) value to return - * @throws IllegalArgumentException if the value passed to v - * is larger than a byte, that is, smaller than -128 or - * larger than 127. - * @return the byte v, unchanged - * @since 3.2 + * @param The type of the reference. + * @param obj The object reference to check for nullity. + * @param message The exception message. + * @return {@code obj} if not {@code null}. + * @throws NullPointerException if {@code obj} is {@code null}. + * @throws IllegalArgumentException if {@code obj} is empty per {@link #isEmpty(Object)}. + * @see #isEmpty(Object) + * @since 3.12.0 */ - public static byte CONST_BYTE(final int v) { - if (v < Byte.MIN_VALUE || v > Byte.MAX_VALUE) { - throw new IllegalArgumentException("Supplied value must be a valid byte literal between -128 and 127: [" + v + "]"); + public static T requireNonEmpty(final T obj, final String message) { + // check for null first to give the most precise exception. + Objects.requireNonNull(obj, message); + if (isEmpty(obj)) { + throw new IllegalArgumentException(message); } - return (byte) v; - } - - /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., - * - *
    -     *     public final static char MAGIC_CHAR = ObjectUtils.CONST('a');
    -     * 
    - * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the char value to return - * @return the char v, unchanged - * @since 3.2 - */ - public static char CONST(final char v) { - return v; + return obj; } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., + * Gets the {@code toString()} of an {@link Object} or the empty string ({@code ""}) if the input is {@code null}. * *
    -     *     public final static short MAGIC_SHORT = ObjectUtils.CONST((short) 123);
    +     * ObjectUtils.toString(null)         = ""
    +     * ObjectUtils.toString("")           = ""
    +     * ObjectUtils.toString("bat")        = "bat"
    +     * ObjectUtils.toString(Boolean.TRUE) = "true"
          * 
    * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the short value to return - * @return the short v, unchanged - * @since 3.2 + * @param obj The Object to {@code toString()}, may be {@code null}. + * @return The input's {@code toString()}, or {@code ""} if the input is {@code null}. + * @see Objects#toString(Object) + * @see Objects#toString(Object, String) + * @see StringUtils#defaultString(String) + * @see String#valueOf(Object) + * @since 2.0 */ - public static short CONST(final short v) { - return v; + public static String toString(final Object obj) { + return Objects.toString(obj, StringUtils.EMPTY); } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., + * Gets the {@code toString} of an {@link Object} returning + * a specified text if {@code null} input. * *
    -     *     public final static short MAGIC_SHORT = ObjectUtils.CONST_SHORT(127);
    +     * ObjectUtils.toString(null, null)           = null
    +     * ObjectUtils.toString(null, "null")         = "null"
    +     * ObjectUtils.toString("", "null")           = ""
    +     * ObjectUtils.toString("bat", "null")        = "bat"
    +     * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
          * 
    * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the short literal (as an int) value to return - * @throws IllegalArgumentException if the value passed to v - * is larger than a short, that is, smaller than -32768 or - * larger than 32767. - * @return the byte v, unchanged - * @since 3.2 + * @param obj The Object to {@code toString}, may be null. + * @param nullStr The String to return if {@code null} input, may be null. + * @return The passed in Object's toString, or {@code nullStr} if {@code null} input. + * @see Objects#toString(Object) + * @see Objects#toString(Object, String) + * @see StringUtils#defaultString(String,String) + * @see String#valueOf(Object) + * @since 2.0 + * @deprecated Replaced by {@code java.util.Objects.toString(Object, String)} in Java 7 and + * will be removed in future releases. */ - public static short CONST_SHORT(final int v) { - if (v < Short.MIN_VALUE || v > Short.MAX_VALUE) { - throw new IllegalArgumentException("Supplied value must be a valid byte literal between -32768 and 32767: [" + v + "]"); - } - return (short) v; + @Deprecated + public static String toString(final Object obj, final String nullStr) { + return Objects.toString(obj, nullStr); } - /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., + * Gets the {@code toString} of an {@link Supplier}'s {@link Supplier#get()} returning + * a specified text if {@code null} input. * - *
    -     *     public final static int MAGIC_INT = ObjectUtils.CONST(123);
    +     * 
    {@code
    +     * ObjectUtils.toString(() -> obj, () -> expensive())
          * 
    - * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the int value to return - * @return the int v, unchanged - * @since 3.2 - */ - public static int CONST(final int v) { - return v; - } - - /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., - * *
    -     *     public final static long MAGIC_LONG = ObjectUtils.CONST(123L);
    -     * 
    - * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the long value to return - * @return the long v, unchanged - * @since 3.2 + * ObjectUtils.toString(() -> null, () -> expensive()) = result of expensive() + * ObjectUtils.toString(() -> null, () -> expensive()) = result of expensive() + * ObjectUtils.toString(() -> "", () -> expensive()) = "" + * ObjectUtils.toString(() -> "bat", () -> expensive()) = "bat" + * ObjectUtils.toString(() -> Boolean.TRUE, () -> expensive()) = "true" + * }
    + * + * @param obj The Object to {@code toString}, may be null. + * @param supplier The Supplier of String used on {@code null} input, may be null. + * @return The passed in Object's toString, or {@code nullStr} if {@code null} input. + * @since 3.14.0 */ - public static long CONST(final long v) { - return v; + public static String toString(final Supplier obj, final Supplier supplier) { + return obj == null ? Suppliers.get(supplier) : toString(obj.get(), supplier); } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., - * - *
    -     *     public final static float MAGIC_FLOAT = ObjectUtils.CONST(1.0f);
    -     * 
    - * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the float value to return - * @return the float v, unchanged - * @since 3.2 + * Gets the {@code toString} of an {@link Object} returning + * a specified text if {@code null} input. + * + *
    {@code
    +     * ObjectUtils.toString(obj, () -> expensive())
    +     * }
    + *
    {@code
    +     * ObjectUtils.toString(null, () -> expensive())         = result of expensive()
    +     * ObjectUtils.toString(null, () -> expensive())         = result of expensive()
    +     * ObjectUtils.toString("", () -> expensive())           = ""
    +     * ObjectUtils.toString("bat", () -> expensive())        = "bat"
    +     * ObjectUtils.toString(Boolean.TRUE, () -> expensive()) = "true"
    +     * }
    + * + * @param The obj type (used to provide better source compatibility in 3.14.0). + * @param obj The Object to {@code toString}, may be null. + * @param supplier The Supplier of String used on {@code null} input, may be null. + * @return The passed in Object's toString, or {@code nullStr} if {@code null} input. + * @since 3.11 */ - public static float CONST(final float v) { - return v; + public static String toString(final T obj, final Supplier supplier) { + return obj == null ? Suppliers.get(supplier) : obj.toString(); } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., - * - *
    -     *     public final static double MAGIC_DOUBLE = ObjectUtils.CONST(1.0);
    -     * 
    - * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. - * - * @param v the double value to return - * @return the double v, unchanged - * @since 3.2 + * Calls {@link Object#wait(long, int)} for the given Duration. + * + * @param obj The receiver of the wait call. + * @param duration How long to wait. + * @throws IllegalArgumentException if the timeout duration is negative. + * @throws IllegalMonitorStateException if the current thread is not the owner of the {@code obj}'s monitor. + * @throws InterruptedException if any thread interrupted the current thread before or while the current thread was + * waiting for a notification. The interrupted status of the current thread is cleared when this + * exception is thrown. + * @see Object#wait(long, int) + * @since 3.12.0 */ - public static double CONST(final double v) { - return v; + public static void wait(final Object obj, final Duration duration) throws InterruptedException { + DurationUtils.accept(obj::wait, DurationUtils.zeroIfNull(duration)); } /** - * This method returns the provided value unchanged. - * This can prevent javac from inlining a constant - * field, e.g., - * - *
    -     *     public final static String MAGIC_STRING = ObjectUtils.CONST("abc");
    -     * 
    + * {@link ObjectUtils} instances should NOT be constructed in standard programming. Instead, the static methods on the class should be used, such as + * {@code ObjectUtils.defaultIfNull("a","b");}. * - * This way any jars that refer to this field do not - * have to recompile themselves if the field's value - * changes at some future date. + *

    + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

    * - * @param the Object type - * @param v the genericized Object value to return (typically a String). - * @return the genericized Object v, unchanged (typically a String). - * @since 3.2 + * @deprecated TODO Make private in 4.0. */ - public static T CONST(final T v) { - return v; + @Deprecated + public ObjectUtils() { + // empty } } diff --git a/src/main/java/org/apache/commons/lang3/RandomStringUtils.java b/src/main/java/org/apache/commons/lang3/RandomStringUtils.java index 0d6df4eed0d..d400e4767b7 100644 --- a/src/main/java/org/apache/commons/lang3/RandomStringUtils.java +++ b/src/main/java/org/apache/commons/lang3/RandomStringUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,463 +16,1064 @@ */ package org.apache.commons.lang3; +import java.security.SecureRandom; +import java.security.Security; import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Supplier; /** - *

    Generates random {@code String}s.

    + * Generates random {@link String}s. + *

    + * Use {@link #secure()} to get the singleton instance based on {@link SecureRandom#SecureRandom()} which uses a secure random number generator implementing the + * default random number algorithm. + *

    + *

    + * Use {@link #secureStrong()} to get the singleton instance based on {@link SecureRandom#getInstanceStrong()} which uses an instance that was selected by using + * the algorithms/providers specified in the {@code securerandom.strongAlgorithms} {@link Security} property. + *

    + *

    + * Use {@link #insecure()} to get the singleton instance based on {@link ThreadLocalRandom#current()} which is not cryptographically secure. In addition, + * instances do not use a cryptographically random seed unless the {@linkplain System#getProperty system property} {@code java.util.secureRandomSeed} is set to + * {@code true}. + *

    + *

    + * Starting in version 3.17.0, the method {@link #secure()} uses {@link SecureRandom#SecureRandom()} instead of {@link SecureRandom#getInstanceStrong()}, and + * adds {@link #secureStrong()}. + *

    + *

    + * Starting in version 3.16.0, this class uses {@link #secure()} for static methods and adds {@link #insecure()}. + *

    + *

    + * Starting in version 3.15.0, this class uses {@link SecureRandom#getInstanceStrong()} for static methods. + *

    + *

    + * Before version 3.15.0, this class used {@link ThreadLocalRandom#current()} for static methods, which is not cryptographically secure. + *

    + *

    + * RandomStringUtils is intended for simple use cases. For more advanced use cases consider using Apache Commons Text's + * RandomStringGenerator + * instead. + *

    + *

    + * The Apache Commons project provides Commons RNG dedicated to pseudo-random number generation, + * that may be a better choice for applications with more stringent requirements (performance and/or correctness). + *

    + *

    + * Note that private high surrogate characters are ignored. These are Unicode characters that fall between the values 56192 (db80) and 56319 (dbff) as + * we don't know how to handle them. High and low surrogates are correctly dealt with - that is if a high surrogate is randomly chosen, 55296 (d800) to 56191 + * (db7f) then it is followed by a low surrogate. If a low surrogate is chosen, 56320 (dc00) to 57343 (dfff) then it is placed after a randomly chosen high + * surrogate. + *

    + *

    + * #ThreadSafe# + *

    * - *

    Caveat: Instances of {@link Random}, upon which the implementation of this - * class relies, are not cryptographically secure.

    - * - *

    RandomStringUtils is intended for simple use cases. For more advanced - * use cases consider using Apache Commons Text's - * - * RandomStringGenerator instead.

    - * - *

    The Apache Commons project provides - * Commons RNG dedicated to pseudo-random number generation, that may be - * a better choice for applications with more stringent requirements - * (performance and/or correctness).

    - * - *

    Note that private high surrogate characters are ignored. - * These are Unicode characters that fall between the values 56192 (db80) - * and 56319 (dbff) as we don't know how to handle them. - * High and low surrogates are correctly dealt with - that is if a - * high surrogate is randomly chosen, 55296 (d800) to 56191 (db7f) - * then it is followed by a low surrogate. If a low surrogate is chosen, - * 56320 (dc00) to 57343 (dfff) then it is placed after a randomly - * chosen high surrogate.

    - * - *

    #ThreadSafe#

    + * @see #secure() + * @see #secureStrong() + * @see #insecure() + * @see SecureRandom#SecureRandom() + * @see SecureRandom#getInstanceStrong() + * @see ThreadLocalRandom#current() + * @see RandomUtils * @since 1.0 */ public class RandomStringUtils { + private static final Supplier SECURE_SUPPLIER = RandomUtils::secure; + + private static final RandomStringUtils INSECURE = new RandomStringUtils(RandomUtils::insecure); + + private static final RandomStringUtils SECURE = new RandomStringUtils(SECURE_SUPPLIER); + + private static final RandomStringUtils SECURE_STRONG = new RandomStringUtils(RandomUtils::secureStrong); + + private static final char[] ALPHANUMERICAL_CHARS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9' }; + + private static final int ASCII_0 = '0'; + private static final int ASCII_9 = '9'; + private static final int ASCII_A = 'A'; + private static final int ASCII_z = 'z'; + + private static final int CACHE_PADDING_BITS = 3; + private static final int BITS_TO_BYTES_DIVISOR = 5; + private static final int BASE_CACHE_SIZE_PADDING = 10; + /** - *

    Random object used by random method. This has to be not local - * to the random method so as to not return the same value in the - * same millisecond.

    + * Gets the singleton instance based on {@link ThreadLocalRandom#current()}; which is not cryptographically + * secure; for more secure processing use {@link #secure()} or {@link #secureStrong()}. + *

    + * The method {@link ThreadLocalRandom#current()} is called on-demand. + *

    + * + * @return The singleton instance based on {@link ThreadLocalRandom#current()}. + * @see ThreadLocalRandom#current() + * @see #secure() + * @see #secureStrong() + * @since 3.16.0 */ - private static final Random RANDOM = new Random(); + public static RandomStringUtils insecure() { + return INSECURE; + } /** - *

    {@code RandomStringUtils} instances should NOT be constructed in - * standard programming. Instead, the class should be used as - * {@code RandomStringUtils.random(5);}.

    + * Creates a random string whose length is the number of characters specified. * - *

    This constructor is public to permit tools that require a JavaBean instance - * to operate.

    + *

    + * Characters will be chosen from the set of all characters. + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #next(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ - public RandomStringUtils() { - super(); + @Deprecated + public static String random(final int count) { + return secure().next(count); } - // Random - //----------------------------------------------------------------------- /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of all characters.

    + *

    + * Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param numbers if {@code true}, generated string may include numeric characters. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #next(int, boolean, boolean)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ - public static String random(final int count) { - return random(count, false, false); + @Deprecated + public static String random(final int count, final boolean letters, final boolean numbers) { + return secure().next(count, letters, numbers); } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of characters whose - * ASCII value is between {@code 32} and {@code 126} (inclusive).

    + *

    + * Characters will be chosen from the set of characters specified. + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @param chars The character array containing the set of characters to use, may be null. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #next(int, char...)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ - public static String randomAscii(final int count) { - return random(count, 32, 127, false, false); + @Deprecated + public static String random(final int count, final char... chars) { + return secure().next(count, chars); } /** - *

    Creates a random string whose length is between the inclusive minimum and - * the exclusive maximum.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of characters whose - * ASCII value is between {@code 32} and {@code 126} (inclusive).

    + *

    + * Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. + *

    * - * @param minLengthInclusive the inclusive minimum length of the string to generate - * @param maxLengthExclusive the exclusive maximum length of the string to generate - * @return the random string - * @since 3.5 + * @param count The length of random string to create. + * @param start The position in set of chars to start at. + * @param end The position in set of chars to end before. + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param numbers if {@code true}, generated string may include numeric characters. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #next(int, int, int, boolean, boolean)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ - public static String randomAscii(final int minLengthInclusive, final int maxLengthExclusive) { - return randomAscii(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + @Deprecated + public static String random(final int count, final int start, final int end, final boolean letters, + final boolean numbers) { + return secure().next(count, start, end, letters, numbers); + } + + /** + * Creates a random string based on a variety of options, using default source of randomness. + * + *

    + * This method has exactly the same semantics as {@link #random(int,int,int,boolean,boolean,char[],Random)}, but + * instead of using an externally supplied source of randomness, it uses the internal static {@link Random} + * instance. + *

    + * + * @param count The length of random string to create. + * @param start The position in set of chars to start at. + * @param end The position in set of chars to end before. + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param numbers if {@code true}, generated string may include numeric characters. + * @param chars The set of chars to choose randoms from. If {@code null}, then it will use the set of all chars. + * @return The random string. + * @throws ArrayIndexOutOfBoundsException if there are not {@code (end - start) + 1} characters in the set array. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #next(int, int, int, boolean, boolean, char...)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static String random(final int count, final int start, final int end, final boolean letters, + final boolean numbers, final char... chars) { + return secure().next(count, start, end, letters, numbers, chars); + } + + /** + * Creates a random string based on a variety of options, using supplied source of randomness. + * + *

    + * If start and end are both {@code 0}, start and end are set to {@code ' '} and {@code 'z'}, the ASCII printable + * characters, will be used, unless letters and numbers are both {@code false}, in which case, start and end are set + * to {@code 0} and {@link Character#MAX_CODE_POINT}. + * + *

    + * If set is not {@code null}, characters between start and end are chosen. + *

    + * + *

    + * This method accepts a user-supplied {@link Random} instance to use as a source of randomness. By seeding a single + * {@link Random} instance with a fixed seed and using it for each call, the same random sequence of strings can be + * generated repeatedly and predictably. + *

    + * + * @param count The length of random string to create. + * @param start The position in set of chars to start at (inclusive). + * @param end The position in set of chars to end before (exclusive). + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param digits if {@code true}, generated string may include digit characters. + * @param chars The set of chars to choose randoms from, must not be empty. If {@code null}, then it will use the + * set of all chars. + * @param random A source of randomness. + * @return The random string. + * @throws ArrayIndexOutOfBoundsException if there are not {@code (end - start) + 1} characters in the set array. + * @throws IllegalArgumentException if {@code count} < 0 or the provided chars array is empty. + * @since 2.0 + */ + public static String random(int count, int start, int end, final boolean letters, final boolean digits, + final char[] chars, final Random random) { + if (count == 0) { + return StringUtils.EMPTY; + } + if (count < 0) { + throw new IllegalArgumentException(String.format("Requested random string length %,d is less than 0.", end)); + } + if (chars != null && chars.length == 0) { + throw new IllegalArgumentException("The chars array must not be empty"); + } + if (start == 0 && end == 0) { + if (chars != null) { + end = chars.length; + } else if (!letters && !digits) { + end = Character.MAX_CODE_POINT; + } else { + end = 'z' + 1; + start = ' '; + } + } else if (end <= start) { + throw new IllegalArgumentException(String.format("Parameter end (%,d) must be greater than start (%,d)", end, start)); + } else if (start < 0 || end < 0) { + throw new IllegalArgumentException("Character positions MUST be >= 0"); + } else if (chars != null && start >= chars.length) { + throw new IllegalArgumentException("start >= chars.length"); + } else if (chars != null && end > chars.length) { + throw new IllegalArgumentException("end > chars.length"); + } + if (end > Character.MAX_CODE_POINT) { + // Technically, it should be `Character.MAX_CODE_POINT+1` as `end` is excluded + // But the character `Character.MAX_CODE_POINT` is private use, so it would anyway be excluded + end = Character.MAX_CODE_POINT; + } + // Optimizations and tests when chars == null and using ASCII characters (end <= 0x7f) + if (chars == null && end <= 0x7f) { + // Optimize generation of full alphanumerical characters + // Normally, we would need to pick a 7-bit integer, since gap = 'z' - '0' + 1 = 75 > 64 + // In turn, this would make us reject the sampling with probability 1 - 62 / 2^7 > 1 / 2 + // Instead we can pick directly from the right set of 62 characters, which requires + // picking a 6-bit integer and only rejecting with probability 2 / 64 = 1 / 32 + if (letters && digits && start <= ASCII_0 && end >= ASCII_z + 1) { + return random(count, 0, 0, false, false, ALPHANUMERICAL_CHARS, random); + } + // Only reject when none of the requested categories is reachable; otherwise a letters && digits + // request would throw on a range that holds one category but not the other (e.g. [ASCII_0, ASCII_A)). + if ((!digits || end <= ASCII_0) && (!letters || end <= ASCII_A) && (digits || letters)) { + throw new IllegalArgumentException( + String.format("Parameter end (%,d) must be greater than (%,d) for generating digits or greater than (%,d) for generating letters.", end, + ASCII_0, ASCII_A)); + } + // Optimize start and end when filtering by letters and/or numbers: + // The range provided may be too large since we filter anyway afterward. + // Note the use of Math.min/max (as opposed to setting start to '0' for example), + // since it is possible the range start/end excludes some of the letters/numbers, + // e.g., it is possible that start already is '1' when numbers = true, and start + // needs to stay equal to '1' in that case. + // Note that because of the above test, we will always have start < end + // even after this optimization. + if (letters && digits) { + start = Math.max(ASCII_0, start); + end = Math.min(ASCII_z + 1, end); + // The clamp can empty the range when it sits above the alphanumerics (e.g. [ASCII_z + 1, 0x7f)), + // unlike the single-category branches below which are validated by the reachability loops further + // down. Reject here so the caller gets a clear range error instead of nextBits(0) failing later. + if (start >= end) { + throw new IllegalArgumentException(String.format("No letters or digits exist between start %,d and end %,d.", start, end)); + } + } else if (digits) { + // just numbers, no letters + start = Math.max(ASCII_0, start); + end = Math.min(ASCII_9 + 1, end); + } else if (letters) { + // just letters, no numbers + start = Math.max(ASCII_A, start); + end = Math.min(ASCII_z + 1, end); + } + } + if (chars == null) { + // start/end are code points: validate using Character.isLetter/isDigit on the + // code-point range rather than on the loop index. + if (letters && !digits) { + boolean ok = false; + for (int i = start; i < end; i++) { + if (Character.isLetter(i)) { + ok = true; + break; + } + } + if (!ok) { + throw new IllegalArgumentException(String.format("No letters exist between start %,d and end %,d.", start, end)); + } + } + if (!letters && digits) { + boolean ok = false; + for (int i = start; i < end; i++) { + if (Character.isDigit(i)) { + ok = true; + break; + } + } + if (!ok) { + throw new IllegalArgumentException(String.format("No digits exist between start %,d and end %,d.", start, end)); + } + } + } else if (letters || digits) { + // chars != null. start/end are indices into chars[]; validate the actual + // chars contain at least one element matching some requested letter/digit + // category to avoid an infinite generation loop when the array lacks every + // requested category. + boolean hasMatch = false; + for (int i = start; i < end; i++) { + final char c = chars[i]; + if (letters && Character.isLetter(c) || digits && Character.isDigit(c)) { + hasMatch = true; + break; + } + } + if (!hasMatch) { + throw new IllegalArgumentException(String.format("No %s%s%s exist in chars[%,d..%,d).", letters ? "letters" : "", + letters && digits ? " or " : "", digits ? "digits" : "", start, end)); + } + } + final StringBuilder builder = new StringBuilder(count); + final int gap = end - start; + final int gapBits = Integer.SIZE - Integer.numberOfLeadingZeros(gap); + // The size of the cache we use is an heuristic: + // about twice the number of bytes required if no rejection + // Ideally the cache size depends on multiple factor, including the cost of generating x bytes + // of randomness as well as the probability of rejection. It is however not easy to know + // those values programmatically for the general case. + // Calculate cache size: + // 1. Multiply count by bits needed per character (gapBits) + // 2. Add padding bits (3) to handle partial bytes + // 3. Divide by 5 to convert to bytes (normally this would be by 8, dividing by 5 allows for about 60% extra space) + // 4. Add base padding (10) to handle small counts efficiently + // 5. Ensure we don't exceed Integer.MAX_VALUE / 5 + 10 to provide a good balance between overflow prevention and + // making the cache extremely large + final long desiredCacheSize = ((long) count * gapBits + CACHE_PADDING_BITS) / BITS_TO_BYTES_DIVISOR + BASE_CACHE_SIZE_PADDING; + final int cacheSize = (int) Math.min(desiredCacheSize, Integer.MAX_VALUE / BITS_TO_BYTES_DIVISOR + BASE_CACHE_SIZE_PADDING); + final CachedRandomBits arb = new CachedRandomBits(cacheSize, random); + // Bound rejection retries so a range that rejects every sample + // (for example, entirely UNASSIGNED/PRIVATE_USE/SURROGATE) raises an + // IllegalArgumentException instead of looping indefinitely. Cap is + // (end - start) * 10 with a small floor so tiny gaps still get a + // reasonable budget. The counter resets on every accepted code point. + final int maxRejections = Math.max(64, gap * 10); + int rejections = 0; + while (count-- != 0) { + // Generate a random value between start (included) and end (excluded) + final int randomValue = arb.nextBits(gapBits) + start; + // Rejection sampling if value too large + if (randomValue >= end) { + count++; + if (++rejections > maxRejections) { + throw new IllegalArgumentException( + String.format("No acceptable code points found in range [%,d, %,d) within %,d attempts.", start, end, maxRejections)); + } + continue; + } + final int codePoint; + if (chars == null) { + codePoint = randomValue; + switch (Character.getType(codePoint)) { + case Character.UNASSIGNED: + case Character.PRIVATE_USE: + case Character.SURROGATE: + count++; + if (++rejections > maxRejections) { + throw new IllegalArgumentException( + String.format("No acceptable code points found in range [%,d, %,d) within %,d attempts.", start, end, maxRejections)); + } + continue; + } + } else { + codePoint = chars[randomValue]; + } + final int numberOfChars = Character.charCount(codePoint); + if (count == 0 && numberOfChars > 1) { + count++; + if (++rejections > maxRejections) { + throw new IllegalArgumentException( + String.format("No acceptable code points found in range [%,d, %,d) within %,d attempts.", start, end, maxRejections)); + } + continue; + } + if (letters && Character.isLetter(codePoint) || digits && Character.isDigit(codePoint) || !letters && !digits) { + builder.appendCodePoint(codePoint); + if (numberOfChars == 2) { + count--; + } + rejections = 0; + } else { + count++; + if (++rejections > maxRejections) { + throw new IllegalArgumentException( + String.format("No acceptable code points found in range [%,d, %,d) within %,d attempts.", start, end, maxRejections)); + } + } + } + return builder.toString(); } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of Latin alphabetic - * characters (a-z, A-Z).

    + *

    + * Characters will be chosen from the set of characters specified by the string, must not be empty. If null, the set + * of all characters is used. + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @param chars The String containing the set of characters to use, may be null, but must not be empty. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0 or the string is empty. + * @deprecated Use {@link #next(int, String)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated + public static String random(final int count, final String chars) { + return secure().next(count, chars); + } + + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z). + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #nextAlphabetic(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated public static String randomAlphabetic(final int count) { - return random(count, true, false); + return secure().nextAlphabetic(count); } /** - *

    Creates a random string whose length is between the inclusive minimum and - * the exclusive maximum.

    + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. * - *

    Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z).

    + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z). + *

    * - * @param minLengthInclusive the inclusive minimum length of the string to generate - * @param maxLengthExclusive the exclusive maximum length of the string to generate - * @return the random string + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. * @since 3.5 + * @deprecated Use {@link #nextAlphabetic(int, int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomAlphabetic(final int minLengthInclusive, final int maxLengthExclusive) { - return randomAlphabetic(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + return secure().nextAlphabetic(minLengthInclusive, maxLengthExclusive); } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of Latin alphabetic - * characters (a-z, A-Z) and the digits 0-9.

    + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z) and the digits 0-9. + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #nextAlphanumeric(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomAlphanumeric(final int count) { - return random(count, true, true); + return secure().nextAlphanumeric(count); } /** - *

    Creates a random string whose length is between the inclusive minimum and - * the exclusive maximum.

    + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. * - *

    Characters will be chosen from the set of Latin alphabetic - * characters (a-z, A-Z) and the digits 0-9.

    + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z) and the digits 0-9. + *

    * - * @param minLengthInclusive the inclusive minimum length of the string to generate - * @param maxLengthExclusive the exclusive maximum length of the string to generate - * @return the random string + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. * @since 3.5 + * @deprecated Use {@link #nextAlphanumeric(int, int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) { - return randomAlphanumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + return secure().nextAlphanumeric(minLengthInclusive, maxLengthExclusive); + } + + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of characters whose ASCII value is between {@code 32} and {@code 126} + * (inclusive). + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #nextAscii(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static String randomAscii(final int count) { + return secure().nextAscii(count); + } + + /** + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. + * + *

    + * Characters will be chosen from the set of characters whose ASCII value is between {@code 32} and {@code 126} + * (inclusive). + *

    + * + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.5 + * @deprecated Use {@link #nextAscii(int, int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static String randomAscii(final int minLengthInclusive, final int maxLengthExclusive) { + return secure().nextAscii(minLengthInclusive, maxLengthExclusive); } /** - *

    Creates a random string whose length is the number of characters specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of characters which match the POSIX [:graph:] - * regular expression character class. This class contains all visible ASCII characters - * (i.e. anything except spaces and control characters).

    + *

    + * Characters will be chosen from the set of characters which match the POSIX [:graph:] regular expression character + * class. This class contains all visible ASCII characters (i.e. anything except spaces and control characters). + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. * @since 3.5 + * @deprecated Use {@link #nextGraph(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomGraph(final int count) { - return random(count, 33, 126, false, false); + return secure().nextGraph(count); } /** - *

    Creates a random string whose length is between the inclusive minimum and - * the exclusive maximum.

    + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. * - *

    Characters will be chosen from the set of \p{Graph} characters.

    + *

    + * Characters will be chosen from the set of \p{Graph} characters. + *

    * - * @param minLengthInclusive the inclusive minimum length of the string to generate - * @param maxLengthExclusive the exclusive maximum length of the string to generate - * @return the random string + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. * @since 3.5 + * @deprecated Use {@link #nextGraph(int, int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomGraph(final int minLengthInclusive, final int maxLengthExclusive) { - return randomGraph(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + return secure().nextGraph(minLengthInclusive, maxLengthExclusive); } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of numeric - * characters.

    + *

    + * Characters will be chosen from the set of numeric characters. + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @deprecated Use {@link #nextNumeric(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomNumeric(final int count) { - return random(count, false, true); + return secure().nextNumeric(count); } /** - *

    Creates a random string whose length is between the inclusive minimum and - * the exclusive maximum.

    + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. * - *

    Characters will be chosen from the set of \p{Digit} characters.

    + *

    + * Characters will be chosen from the set of \p{Digit} characters. + *

    * - * @param minLengthInclusive the inclusive minimum length of the string to generate - * @param maxLengthExclusive the exclusive maximum length of the string to generate - * @return the random string + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. * @since 3.5 + * @deprecated Use {@link #nextNumeric(int, int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomNumeric(final int minLengthInclusive, final int maxLengthExclusive) { - return randomNumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + return secure().nextNumeric(minLengthInclusive, maxLengthExclusive); } /** - *

    Creates a random string whose length is the number of characters specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of characters which match the POSIX [:print:] - * regular expression character class. This class includes all visible ASCII characters and spaces - * (i.e. anything except control characters).

    + *

    + * Characters will be chosen from the set of characters which match the POSIX [:print:] regular expression character + * class. This class includes all visible ASCII characters and spaces (i.e. anything except control characters). + *

    * - * @param count the length of random string to create - * @return the random string + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. * @since 3.5 + * @deprecated Use {@link #nextPrint(int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomPrint(final int count) { - return random(count, 32, 126, false, false); + return secure().nextPrint(count); } /** - *

    Creates a random string whose length is between the inclusive minimum and - * the exclusive maximum.

    + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. * - *

    Characters will be chosen from the set of \p{Print} characters.

    + *

    + * Characters will be chosen from the set of \p{Print} characters. + *

    * - * @param minLengthInclusive the inclusive minimum length of the string to generate - * @param maxLengthExclusive the exclusive maximum length of the string to generate - * @return the random string + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. * @since 3.5 + * @deprecated Use {@link #nextPrint(int, int)} from {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static String randomPrint(final int minLengthInclusive, final int maxLengthExclusive) { - return randomPrint(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + return secure().nextPrint(minLengthInclusive, maxLengthExclusive); } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Gets the singleton instance based on {@link SecureRandom#SecureRandom()} which uses a secure random number generator (RNG) implementing the default + * random number algorithm. + *

    + * The method {@link SecureRandom#SecureRandom()} is called on-demand. + *

    * - *

    Characters will be chosen from the set of alpha-numeric - * characters as indicated by the arguments.

    + * @return The singleton instance based on {@link SecureRandom#SecureRandom()}. + * @see SecureRandom#SecureRandom() + * @since 3.16.0 + */ + public static RandomStringUtils secure() { + return SECURE; + } + + /** + * Gets the singleton instance based on {@link SecureRandom#getInstanceStrong()} which uses an algorithms/providers + * specified in the {@code securerandom.strongAlgorithms} {@link Security} property. + *

    + * The method {@link SecureRandom#getInstanceStrong()} is called on-demand. + *

    * - * @param count the length of random string to create - * @param letters if {@code true}, generated string may include - * alphabetic characters - * @param numbers if {@code true}, generated string may include - * numeric characters - * @return the random string + * @return The singleton instance based on {@link SecureRandom#getInstanceStrong()}. + * @see SecureRandom#getInstanceStrong() + * @since 3.17.0 */ - public static String random(final int count, final boolean letters, final boolean numbers) { - return random(count, 0, 0, letters, numbers); + public static RandomStringUtils secureStrong() { + return SECURE_STRONG; } + private final Supplier random; + /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * {@link RandomStringUtils} instances should NOT be constructed in standard programming. Instead, the class should + * be used as {@code RandomStringUtils.random(5);}. * - *

    Characters will be chosen from the set of alpha-numeric - * characters as indicated by the arguments.

    + *

    + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

    * - * @param count the length of random string to create - * @param start the position in set of chars to start at - * @param end the position in set of chars to end before - * @param letters if {@code true}, generated string may include - * alphabetic characters - * @param numbers if {@code true}, generated string may include - * numeric characters - * @return the random string + * @deprecated TODO Make private in 4.0. */ - public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers) { - return random(count, start, end, letters, numbers, null, RANDOM); + @Deprecated + public RandomStringUtils() { + this(SECURE_SUPPLIER); + } + + private RandomStringUtils(final Supplier random) { + this.random = random; } /** - *

    Creates a random string based on a variety of options, using - * default source of randomness.

    + * Creates a random string whose length is the number of characters specified. * - *

    This method has exactly the same semantics as - * {@link #random(int,int,int,boolean,boolean,char[],Random)}, but - * instead of using an externally supplied source of randomness, it uses - * the internal static {@link Random} instance.

    + *

    + * Characters will be chosen from the set of all characters. + *

    * - * @param count the length of random string to create - * @param start the position in set of chars to start at - * @param end the position in set of chars to end before - * @param letters only allow letters? - * @param numbers only allow numbers? - * @param chars the set of chars to choose randoms from. - * If {@code null}, then it will use the set of all chars. - * @return the random string - * @throws ArrayIndexOutOfBoundsException if there are not - * {@code (end - start) + 1} characters in the set array. + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @since 3.16.0 */ - public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers, final char... chars) { - return random(count, start, end, letters, numbers, chars, RANDOM); + public String next(final int count) { + return next(count, false, false); } /** - *

    Creates a random string based on a variety of options, using - * supplied source of randomness.

    + * Creates a random string whose length is the number of characters specified. * - *

    If start and end are both {@code 0}, start and end are set - * to {@code ' '} and {@code 'z'}, the ASCII printable - * characters, will be used, unless letters and numbers are both - * {@code false}, in which case, start and end are set to - * {@code 0} and {@link Character#MAX_CODE_POINT}. + *

    + * Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. + *

    * - *

    If set is not {@code null}, characters between start and - * end are chosen.

    + * @param count The length of random string to create. + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param numbers if {@code true}, generated string may include numeric characters. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @since 3.16.0 + */ + public String next(final int count, final boolean letters, final boolean numbers) { + return next(count, 0, 0, letters, numbers); + } + + /** + * Creates a random string whose length is the number of characters specified. * - *

    This method accepts a user-supplied {@link Random} - * instance to use as a source of randomness. By seeding a single - * {@link Random} instance with a fixed seed and using it for each call, - * the same random sequence of strings can be generated repeatedly - * and predictably.

    + *

    + * Characters will be chosen from the set of characters specified. + *

    * - * @param count the length of random string to create - * @param start the position in set of chars to start at (inclusive) - * @param end the position in set of chars to end before (exclusive) - * @param letters only allow letters? - * @param numbers only allow numbers? - * @param chars the set of chars to choose randoms from, must not be empty. - * If {@code null}, then it will use the set of all chars. - * @param random a source of randomness. - * @return the random string - * @throws ArrayIndexOutOfBoundsException if there are not - * {@code (end - start) + 1} characters in the set array. - * @throws IllegalArgumentException if {@code count} < 0 or the provided chars array is empty. - * @since 2.0 + * @param count The length of random string to create. + * @param chars The character array containing the set of characters to use, may be null. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @since 3.16.0 */ - public static String random(int count, int start, int end, final boolean letters, final boolean numbers, - final char[] chars, final Random random) { - if (count == 0) { - return StringUtils.EMPTY; - } else if (count < 0) { - throw new IllegalArgumentException("Requested random string length " + count + " is less than 0."); - } - if (chars != null && chars.length == 0) { - throw new IllegalArgumentException("The chars array must not be empty"); + public String next(final int count, final char... chars) { + if (chars == null) { + return random(count, 0, 0, false, false, null, random()); } + return random(count, 0, chars.length, false, false, chars, random()); + } - if (start == 0 && end == 0) { - if (chars != null) { - end = chars.length; - } else { - if (!letters && !numbers) { - end = Character.MAX_CODE_POINT; - } else { - end = 'z' + 1; - start = ' '; - } - } - } else { - if (end <= start) { - throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")"); - } - } + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. + *

    + * + * @param count The length of random string to create. + * @param start The position in set of chars to start at. + * @param end The position in set of chars to end before. + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param numbers if {@code true}, generated string may include numeric characters. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @since 3.16.0 + */ + public String next(final int count, final int start, final int end, final boolean letters, final boolean numbers) { + return random(count, start, end, letters, numbers, null, random()); + } - final int zero_digit_ascii = 48; - final int first_letter_ascii = 65; + /** + * Creates a random string based on a variety of options, using default source of randomness. + * + *

    + * This method has exactly the same semantics as {@link #random(int,int,int,boolean,boolean,char[],Random)}, but + * instead of using an externally supplied source of randomness, it uses the internal static {@link Random} + * instance. + *

    + * + * @param count The length of random string to create. + * @param start The position in set of chars to start at. + * @param end The position in set of chars to end before. + * @param letters if {@code true}, generated string may include alphabetic characters. + * @param numbers if {@code true}, generated string may include numeric characters. + * @param chars The set of chars to choose randoms from. If {@code null}, then it will use the set of all chars. + * @return The random string. + * @throws ArrayIndexOutOfBoundsException if there are not {@code (end - start) + 1} characters in the set array. + * @throws IllegalArgumentException if {@code count} < 0. + */ + public String next(final int count, final int start, final int end, final boolean letters, final boolean numbers, + final char... chars) { + return random(count, start, end, letters, numbers, chars, random()); + } - if (chars == null && (numbers && end <= zero_digit_ascii - || letters && end <= first_letter_ascii)) { - throw new IllegalArgumentException("Parameter end (" + end + ") must be greater then (" + zero_digit_ascii + ") for generating digits " + - "or greater then (" + first_letter_ascii + ") for generating letters."); + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of characters specified by the string, must not be empty. If null, the set + * of all characters is used. + *

    + * + * @param count The length of random string to create. + * @param chars The String containing the set of characters to use, may be null, but must not be empty. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0 or the string is empty. + * @since 3.16.0 + */ + public String next(final int count, final String chars) { + if (chars == null) { + return random(count, 0, 0, false, false, null, random()); } + return next(count, chars.toCharArray()); + } - final StringBuilder builder = new StringBuilder(count); - final int gap = end - start; + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z). + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + */ + public String nextAlphabetic(final int count) { + return next(count, true, false); + } - while (count-- != 0) { - int codePoint; - if (chars == null) { - codePoint = random.nextInt(gap) + start; + /** + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. + * + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z). + *

    + * + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.5 + */ + public String nextAlphabetic(final int minLengthInclusive, final int maxLengthExclusive) { + return nextAlphabetic(randomUtils().randomInt(minLengthInclusive, maxLengthExclusive)); + } - switch (Character.getType(codePoint)) { - case Character.UNASSIGNED: - case Character.PRIVATE_USE: - case Character.SURROGATE: - count++; - continue; - } + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z) and the digits 0-9. + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + */ + public String nextAlphanumeric(final int count) { + return next(count, true, true); + } - } else { - codePoint = chars[random.nextInt(gap) + start]; - } + /** + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. + * + *

    + * Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z) and the digits 0-9. + *

    + * + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.5 + */ + public String nextAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) { + return nextAlphanumeric(randomUtils().randomInt(minLengthInclusive, maxLengthExclusive)); + } - final int numberOfChars = Character.charCount(codePoint); - if (count == 0 && numberOfChars > 1) { - count++; - continue; - } + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of characters whose ASCII value is between {@code 32} and {@code 126} + * (inclusive). + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + */ + public String nextAscii(final int count) { + return next(count, 32, 127, false, false); + } - if (letters && Character.isLetter(codePoint) - || numbers && Character.isDigit(codePoint) - || !letters && !numbers) { - builder.appendCodePoint(codePoint); + /** + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. + * + *

    + * Characters will be chosen from the set of characters whose ASCII value is between {@code 32} and {@code 126} + * (inclusive). + *

    + * + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.5 + */ + public String nextAscii(final int minLengthInclusive, final int maxLengthExclusive) { + return nextAscii(randomUtils().randomInt(minLengthInclusive, maxLengthExclusive)); + } - if (numberOfChars == 2) { - count--; - } + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of characters which match the POSIX [:graph:] regular expression character + * class. This class contains all visible ASCII characters (i.e. anything except spaces and control characters). + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + * @since 3.5 + */ + public String nextGraph(final int count) { + return next(count, 33, 126, false, false); + } - } else { - count++; - } - } - return builder.toString(); + /** + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. + * + *

    + * Characters will be chosen from the set of \p{Graph} characters. + *

    + * + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.5 + */ + public String nextGraph(final int minLengthInclusive, final int maxLengthExclusive) { + return nextGraph(randomUtils().randomInt(minLengthInclusive, maxLengthExclusive)); } + /** + * Creates a random string whose length is the number of characters specified. + * + *

    + * Characters will be chosen from the set of numeric characters. + *

    + * + * @param count The length of random string to create. + * @return The random string. + * @throws IllegalArgumentException if {@code count} < 0. + */ + public String nextNumeric(final int count) { + return next(count, false, true); + } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. * - *

    Characters will be chosen from the set of characters - * specified by the string, must not be empty. - * If null, the set of all characters is used.

    + *

    + * Characters will be chosen from the set of \p{Digit} characters. + *

    * - * @param count the length of random string to create - * @param chars the String containing the set of characters to use, - * may be null, but must not be empty - * @return the random string - * @throws IllegalArgumentException if {@code count} < 0 or the string is empty. + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.5 */ - public static String random(final int count, final String chars) { - if (chars == null) { - return random(count, 0, 0, false, false, null, RANDOM); - } - return random(count, chars.toCharArray()); + public String nextNumeric(final int minLengthInclusive, final int maxLengthExclusive) { + return nextNumeric(randomUtils().randomInt(minLengthInclusive, maxLengthExclusive)); } /** - *

    Creates a random string whose length is the number of characters - * specified.

    + * Creates a random string whose length is the number of characters specified. * - *

    Characters will be chosen from the set of characters specified.

    + *

    + * Characters will be chosen from the set of characters which match the POSIX [:print:] regular expression character + * class. This class includes all visible ASCII characters and spaces (i.e. anything except control characters). + *

    * - * @param count the length of random string to create - * @param chars the character array containing the set of characters to use, - * may be null - * @return the random string + * @param count The length of random string to create. + * @return The random string. * @throws IllegalArgumentException if {@code count} < 0. + * @since 3.5 + * @since 3.16.0 */ - public static String random(final int count, final char... chars) { - if (chars == null) { - return random(count, 0, 0, false, false, null, RANDOM); - } - return random(count, 0, chars.length, false, false, chars, RANDOM); + public String nextPrint(final int count) { + return next(count, 32, 126, false, false); + } + + /** + * Creates a random string whose length is between the inclusive minimum and the exclusive maximum. + * + *

    + * Characters will be chosen from the set of \p{Print} characters. + *

    + * + * @param minLengthInclusive The inclusive minimum length of the string to generate. + * @param maxLengthExclusive The exclusive maximum length of the string to generate. + * @return The random string. + * @since 3.16.0 + */ + public String nextPrint(final int minLengthInclusive, final int maxLengthExclusive) { + return nextPrint(randomUtils().randomInt(minLengthInclusive, maxLengthExclusive)); + } + + /** + * Gets the Random. + * + * @return The Random. + */ + private Random random() { + return randomUtils().random(); + } + + /** + * Gets the RandomUtils. + * + * @return The RandomUtils. + */ + private RandomUtils randomUtils() { + return random.get(); + } + + @Override + public String toString() { + return "RandomStringUtils [random=" + random() + "]"; } } diff --git a/src/main/java/org/apache/commons/lang3/RandomUtils.java b/src/main/java/org/apache/commons/lang3/RandomUtils.java index 571cece995a..320e3787d14 100644 --- a/src/main/java/org/apache/commons/lang3/RandomUtils.java +++ b/src/main/java/org/apache/commons/lang3/RandomUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,220 +16,445 @@ */ package org.apache.commons.lang3; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.Security; import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Supplier; + +import org.apache.commons.lang3.exception.UncheckedException; /** - *

    Utility library that supplements the standard {@link Random} class.

    - * - *

    Caveat: Instances of {@link Random} are not cryptographically secure.

    - * - *

    Please note that the Apache Commons project provides a component - * dedicated to pseudo-random number generation, namely - * Commons RNG, that may be - * a better choice for applications with more stringent requirements - * (performance and/or correctness).

    + * Supplements the standard {@link Random} class. + *

    + * Use {@link #secure()} to get the singleton instance based on {@link SecureRandom#SecureRandom()} which uses a secure random number generator implementing the + * default random number algorithm. + *

    + *

    + * Use {@link #secureStrong()} to get the singleton instance based on {@link SecureRandom#getInstanceStrong()} which uses an instance that was selected by using + * the algorithms/providers specified in the {@code securerandom.strongAlgorithms} {@link Security} property. + *

    + *

    + * Use {@link #insecure()} to get the singleton instance based on {@link ThreadLocalRandom#current()} which is not cryptographically secure. In addition, + * instances do not use a cryptographically random seed unless the {@linkplain System#getProperty system property} {@code java.util.secureRandomSeed} is set to + * {@code true}. + *

    + *

    + * Starting in version 3.17.0, the method {@link #secure()} uses {@link SecureRandom#SecureRandom()} instead of {@link SecureRandom#getInstanceStrong()}, and + * adds {@link #secureStrong()}. + *

    + *

    + * Starting in version 3.16.0, this class uses {@link #secure()} for static methods and adds {@link #insecure()}. + *

    + *

    + * Starting in version 3.15.0, this class uses {@link SecureRandom#getInstanceStrong()} for static methods. + *

    + *

    + * Before version 3.15.0, this class used {@link ThreadLocalRandom#current()} for static methods, which is not cryptographically secure. + *

    + *

    + * Please note that the Apache Commons project provides a component dedicated to pseudo-random number generation, namely + * Commons RNG, that may be a better choice for applications with more stringent requirements + * (performance and/or correctness). + *

    * + * @see #secure() + * @see #secureStrong() + * @see #insecure() + * @see SecureRandom#SecureRandom() + * @see SecureRandom#getInstanceStrong() + * @see ThreadLocalRandom#current() + * @see RandomStringUtils * @since 3.3 */ public class RandomUtils { - /** - * Random object used by random method. This has to be not local to the - * random method so as to not return the same value in the same millisecond. - */ - private static final Random RANDOM = new Random(); + private static final RandomUtils INSECURE = new RandomUtils(ThreadLocalRandom::current); + + private static final RandomUtils SECURE = new RandomUtils(SecureRandom::new); + + private static final Supplier SECURE_STRONG_SUPPLIER = () -> RandomUtils.SECURE_RANDOM_STRONG.get(); + + private static final RandomUtils SECURE_STRONG = new RandomUtils(SECURE_STRONG_SUPPLIER); + + private static final ThreadLocal SECURE_RANDOM_STRONG = ThreadLocal.withInitial(() -> { + try { + return SecureRandom.getInstanceStrong(); + } catch (final NoSuchAlgorithmException e) { + throw new UncheckedException(e); + } + }); /** + * Gets the singleton instance based on {@link ThreadLocalRandom#current()}; which is not cryptographically + * secure; for more secure processing use {@link #secure()} or {@link #secureStrong()}. *

    - * {@code RandomUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code RandomUtils.nextBytes(5);}. + * The method {@link ThreadLocalRandom#current()} is called on-demand. *

    * - *

    - * This constructor is public to permit tools that require a JavaBean - * instance to operate. - *

    + * @return The singleton instance based on {@link ThreadLocalRandom#current()}. + * @see ThreadLocalRandom#current() + * @see #secure() + * @see #secureStrong() + * @since 3.17.0 */ - public RandomUtils() { - super(); + public static RandomUtils insecure() { + return INSECURE; } /** - *

    - * Returns a random boolean value - *

    + * Generates a random boolean value. * - * @return the random boolean + * @return The random boolean. * @since 3.5 + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static boolean nextBoolean() { - return RANDOM.nextBoolean(); + return secure().randomBoolean(); } /** - *

    - * Creates an array of random bytes. - *

    + * Generates an array of random bytes. * - * @param count - * the size of the returned array - * @return the random byte array - * @throws IllegalArgumentException if {@code count} is negative + * @param count The size of the returned array. + * @return The random byte array. + * @throws IllegalArgumentException if {@code count} is negative. + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static byte[] nextBytes(final int count) { - Validate.isTrue(count >= 0, "Count cannot be negative."); + return secure().randomBytes(count); + } - final byte[] result = new byte[count]; - RANDOM.nextBytes(result); - return result; + /** + * Generates a random double between 0 (inclusive) and {@link Double#MAX_VALUE} ({@value Double#MAX_VALUE} exclusive). + * + * @return The random double. + * @see #nextDouble(double, double) + * @since 3.5 + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static double nextDouble() { + return secure().randomDouble(); } /** - *

    - * Returns a random integer within the specified range. - *

    + * Generates a random double within the specified range. * - * @param startInclusive - * the smallest value that can be returned, must be non-negative - * @param endExclusive - * the upper bound (not included) - * @throws IllegalArgumentException - * if {@code startInclusive > endExclusive} or if - * {@code startInclusive} is negative - * @return the random integer + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random double. + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ - public static int nextInt(final int startInclusive, final int endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, - "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + @Deprecated + public static double nextDouble(final double startInclusive, final double endExclusive) { + return secure().randomDouble(startInclusive, endExclusive); + } - if (startInclusive == endExclusive) { - return startInclusive; - } + /** + * Generates a random float between 0 (inclusive) and {@link Float#MAX_VALUE} ({@value Float#MAX_VALUE} exclusive). + * + * @return The random float. + * @see #nextFloat(float, float) + * @since 3.5 + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static float nextFloat() { + return secure().randomFloat(); + } - return startInclusive + RANDOM.nextInt(endExclusive - startInclusive); + /** + * Generates a random float within the specified range. + * + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random float. + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static float nextFloat(final float startInclusive, final float endExclusive) { + return secure().randomFloat(startInclusive, endExclusive); } /** - *

    Returns a random int within 0 - Integer.MAX_VALUE

    + * Generates a random int between 0 (inclusive) and {@link Integer#MAX_VALUE} ({@value Integer#MAX_VALUE} exclusive). * - * @return the random integer + * @return The random integer. * @see #nextInt(int, int) * @since 3.5 + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static int nextInt() { - return nextInt(0, Integer.MAX_VALUE); + return secure().randomInt(); } /** - *

    - * Returns a random long within the specified range. - *

    + * Generates a random integer within the specified range. * - * @param startInclusive - * the smallest value that can be returned, must be non-negative - * @param endExclusive - * the upper bound (not included) - * @throws IllegalArgumentException - * if {@code startInclusive > endExclusive} or if - * {@code startInclusive} is negative - * @return the random long + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random integer. + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ - public static long nextLong(final long startInclusive, final long endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, - "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); - - if (startInclusive == endExclusive) { - return startInclusive; - } - - return (long) nextDouble(startInclusive, endExclusive); + @Deprecated + public static int nextInt(final int startInclusive, final int endExclusive) { + return secure().randomInt(startInclusive, endExclusive); } /** - *

    Returns a random long within 0 - Long.MAX_VALUE

    + * Generates a random long between 0 (inclusive) and {@link Long#MAX_VALUE} ({@value Long#MAX_VALUE} exclusive). * - * @return the random long + * @return The random long. * @see #nextLong(long, long) * @since 3.5 + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. */ + @Deprecated public static long nextLong() { - return nextLong(0, Long.MAX_VALUE); + return secure().randomLong(); } /** + * Generates a random long within the specified range. + * + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random long. + * @deprecated Use {@link #secure()}, {@link #secureStrong()}, or {@link #insecure()}. + */ + @Deprecated + public static long nextLong(final long startInclusive, final long endExclusive) { + return secure().randomLong(startInclusive, endExclusive); + } + + /** + * Gets the singleton instance based on {@link SecureRandom#SecureRandom()} which uses the default algorithm + * and provider of {@link SecureRandom}. *

    - * Returns a random double within the specified range. + * The method {@link SecureRandom#SecureRandom()} is called on-demand. *

    * - * @param startInclusive - * the smallest value that can be returned, must be non-negative - * @param endExclusive - * the upper bound (not included) - * @throws IllegalArgumentException - * if {@code startInclusive > endExclusive} or if - * {@code startInclusive} is negative - * @return the random double + * @return The singleton instance based on {@link SecureRandom#SecureRandom()}. + * @see SecureRandom#SecureRandom() + * @since 3.16.0 */ - public static double nextDouble(final double startInclusive, final double endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, - "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + public static RandomUtils secure() { + return SECURE; + } + + static SecureRandom secureRandom() { + return SECURE_RANDOM_STRONG.get(); + } + + /** + * Gets the singleton instance based on {@link SecureRandom#getInstanceStrong()} which uses an algorithms/providers + * specified in the {@code securerandom.strongAlgorithms} {@link Security} property. + *

    + * The method {@link SecureRandom#getInstanceStrong()} is called on-demand. + *

    + * + * @return The singleton instance based on {@link SecureRandom#getInstanceStrong()}. + * @see SecureRandom#getInstanceStrong() + * @since 3.17.0 + */ + public static RandomUtils secureStrong() { + return SECURE_STRONG; + } + private final Supplier random; + + /** + * {@link RandomUtils} instances should NOT be constructed in standard programming. Instead, the class should be + * used as {@code RandomUtils.nextBytes(5);}. + *

    + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

    + * + * @deprecated TODO Make private in 4.0. + */ + @Deprecated + public RandomUtils() { + this(SECURE_STRONG_SUPPLIER); + } + + private RandomUtils(final Supplier random) { + this.random = random; + } + + Random random() { + return random.get(); + } + + /** + * Generates a random boolean value. + * + * @return The random boolean. + * @since 3.16.0 + */ + public boolean randomBoolean() { + return random().nextBoolean(); + } + + /** + * Generates an array of random bytes. + * + * @param count The size of the returned array. + * @return The random byte array. + * @throws IllegalArgumentException if {@code count} is negative. + * @since 3.16.0 + */ + public byte[] randomBytes(final int count) { + Validate.isTrue(count >= 0, "Count cannot be negative."); + final byte[] result = new byte[count]; + random().nextBytes(result); + return result; + } + + /** + * Generates a random double between 0 (inclusive) and {@link Double#MAX_VALUE} ({@value Double#MAX_VALUE} exclusive). + * + * @return The random double. + * @see #randomDouble(double, double) + * @since 3.16.0 + */ + public double randomDouble() { + return randomDouble(0, Double.MAX_VALUE); + } + + /** + * Generates a random double within the specified range. + * + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random double. + * @since 3.16.0 + */ + public double randomDouble(final double startInclusive, final double endExclusive) { + Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); if (startInclusive == endExclusive) { return startInclusive; } - - return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextDouble()); + return startInclusive + (endExclusive - startInclusive) * random().nextDouble(); } /** - *

    Returns a random double within 0 - Double.MAX_VALUE

    + * Generates a random float between 0 (inclusive) and Float.MAX_VALUE (exclusive). * - * @return the random double - * @see #nextDouble(double, double) - * @since 3.5 + * @return The random float. + * @see #randomFloat(float, float) + * @since 3.16.0 */ - public static double nextDouble() { - return nextDouble(0, Double.MAX_VALUE); + public float randomFloat() { + return randomFloat(0, Float.MAX_VALUE); } /** - *

    - * Returns a random float within the specified range. - *

    + * Generates a random float within the specified range. * - * @param startInclusive - * the smallest value that can be returned, must be non-negative - * @param endExclusive - * the upper bound (not included) - * @throws IllegalArgumentException - * if {@code startInclusive > endExclusive} or if - * {@code startInclusive} is negative - * @return the random float + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random float. + * @since 3.16.0 */ - public static float nextFloat(final float startInclusive, final float endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, - "Start value must be smaller or equal to end value."); + public float randomFloat(final float startInclusive, final float endExclusive) { + Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + if (startInclusive == endExclusive) { + return startInclusive; + } + return startInclusive + (endExclusive - startInclusive) * random().nextFloat(); + } + /** + * Generates a random int between 0 (inclusive) and {@link Integer#MAX_VALUE} ({@value Integer#MAX_VALUE} exclusive). + * + * @return The random integer. + * @see #randomInt(int, int) + * @since 3.16.0 + */ + public int randomInt() { + return randomInt(0, Integer.MAX_VALUE); + } + + /** + * Generates a random integer within the specified range. + * + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random integer. + * @since 3.16.0 + */ + public int randomInt(final int startInclusive, final int endExclusive) { + Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); if (startInclusive == endExclusive) { return startInclusive; } + return startInclusive + random().nextInt(endExclusive - startInclusive); + } - return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextFloat()); + /** + * Generates a random long between 0 (inclusive) and {@link Long#MAX_VALUE} ({@value Long#MAX_VALUE}, exclusive). + * + * @return The random long. + * @see #randomLong(long, long) + * @since 3.16.0 + */ + public long randomLong() { + return randomLong(Long.MAX_VALUE); } /** - *

    Returns a random float within 0 - Float.MAX_VALUE

    + * Generates a {@code long} value between 0 (inclusive) and the specified value (exclusive). * - * @return the random float - * @see #nextFloat(float, float) - * @since 3.5 + * @param n Bound on the random number to be returned. Must be positive. + * @return A random {@code long} value between 0 (inclusive) and {@code n} (exclusive). */ - public static float nextFloat() { - return nextFloat(0, Float.MAX_VALUE); + private long randomLong(final long n) { + // Extracted from o.a.c.rng.core.BaseProvider.nextLong(long) + long bits; + long val; + do { + bits = random().nextLong() >>> 1; + val = bits % n; + } while (bits - val + n - 1 < 0); + return val; + } + + /** + * Generates a random long within the specified range. + * + * @param startInclusive The smallest value that can be returned, must be non-negative. + * @param endExclusive The upper bound (not included). + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if {@code startInclusive} is negative. + * @return The random long. + * @since 3.16.0 + */ + public long randomLong(final long startInclusive, final long endExclusive) { + Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + if (startInclusive == endExclusive) { + return startInclusive; + } + return startInclusive + randomLong(endExclusive - startInclusive); + } + + @Override + public String toString() { + return "RandomUtils [random=" + random() + "]"; } + } diff --git a/src/main/java/org/apache/commons/lang3/Range.java b/src/main/java/org/apache/commons/lang3/Range.java index 628653acb9a..bf2c456b8d0 100644 --- a/src/main/java/org/apache/commons/lang3/Range.java +++ b/src/main/java/org/apache/commons/lang3/Range.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,32 +16,37 @@ */ package org.apache.commons.lang3; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Comparator; +import java.util.Objects; /** - *

    An immutable range of objects from a minimum to maximum point inclusive.

    + * An immutable range of objects from a minimum to maximum point inclusive. * - *

    The objects need to either be implementations of {@code Comparable} - * or you need to supply a {@code Comparator}.

    + *

    The objects need to either be implementations of {@link Comparable} + * or you need to supply a {@link Comparator}.

    * - *

    #ThreadSafe# if the objects and comparator are thread-safe

    + *

    #ThreadSafe# if the objects and comparator are thread-safe.

    * * @param The type of range values. * @since 3.0 + * @since 3.21.0 {@code serialVersionUID} changed from {@code 1L} to {@code 2L}. */ -public final class Range implements Serializable { +public class Range implements Serializable { - //----------------------------------------------------------------------- @SuppressWarnings({"rawtypes", "unchecked"}) private enum ComparableComparator implements Comparator { INSTANCE; + /** * Comparable based compare implementation. * - * @param obj1 left hand side of comparison - * @param obj2 right hand side of comparison - * @return negative, 0, positive comparison value + * @param obj1 left-hand side of comparison. + * @param obj2 right-hand side of comparison. + * @return negative, 0, positive comparison value. */ @Override public int compare(final Object obj1, final Object obj2) { @@ -51,80 +56,173 @@ public int compare(final Object obj1, final Object obj2) { /** * Serialization version. + * * @see java.io.Serializable + * @since 3.21.0 {@code serialVersionUID} changed from {@code 1L} to {@value}. */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; + /** - *

    Obtains a range with the specified minimum and maximum values (both inclusive).

    + * Creates a range with the specified minimum and maximum values (both inclusive). * *

    The range uses the natural ordering of the elements to determine where * values lie in the range.

    * - *

    The arguments may be passed in the order (min,max) or (max,min). + *

    The arguments may be passed in the order (min, max) or (max, min). * The getMinimum and getMaximum methods will return the correct values.

    * - * @param the type of the elements in this range - * @param fromInclusive the first value that defines the edge of the range, inclusive - * @param toInclusive the second value that defines the edge of the range, inclusive - * @return the range object, not null - * @throws IllegalArgumentException if either element is null - * @throws ClassCastException if the elements are not {@code Comparable} + * @param The type of the elements in this range. + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + * @throws NullPointerException when fromInclusive is null. + * @throws NullPointerException when toInclusive is null. + * @throws ClassCastException if the elements are not {@link Comparable}. + * @throws IllegalArgumentException if either element is a floating-point NaN. + * @deprecated Use {@link #of(Comparable, Comparable)}. */ - public static > Range between(final T fromInclusive, final T toInclusive) { - return between(fromInclusive, toInclusive, null); + @Deprecated + public static > Range between(final T fromInclusive, final T toInclusive) { + return of(fromInclusive, toInclusive, null); } + /** - *

    Obtains a range with the specified minimum and maximum values (both inclusive).

    + * Creates a range with the specified minimum and maximum values (both inclusive). * - *

    The range uses the specified {@code Comparator} to determine where + *

    The range uses the specified {@link Comparator} to determine where * values lie in the range.

    * - *

    The arguments may be passed in the order (min,max) or (max,min). + *

    The arguments may be passed in the order (min, max) or (max, min). * The getMinimum and getMaximum methods will return the correct values.

    * - * @param the type of the elements in this range - * @param fromInclusive the first value that defines the edge of the range, inclusive - * @param toInclusive the second value that defines the edge of the range, inclusive - * @param comparator the comparator to be used, null for natural ordering - * @return the range object, not null - * @throws IllegalArgumentException if either element is null - * @throws ClassCastException if using natural ordering and the elements are not {@code Comparable} + * @param The type of the elements in this range. + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @param comparator The comparator to be used, null for natural ordering. + * @return The range object, not null. + * @throws NullPointerException when fromInclusive is null. + * @throws NullPointerException when toInclusive is null. + * @throws ClassCastException if using natural ordering and the elements are not {@link Comparable}. + * @throws IllegalArgumentException if either element is a floating-point NaN. + * @deprecated Use {@link #of(Object, Object, Comparator)}. */ + @Deprecated public static Range between(final T fromInclusive, final T toInclusive, final Comparator comparator) { return new Range<>(fromInclusive, toInclusive, comparator); } + + private static int hash(final Object value1, final Object value2) { + return Objects.hash(value1, value2); + } + /** - *

    Obtains a range using the specified element as both the minimum - * and maximum in this range.

    + * Creates a range using the specified element as both the minimum + * and maximum in this range. * *

    The range uses the natural ordering of the elements to determine where * values lie in the range.

    * - * @param the type of the elements in this range - * @param element the value to use for this range, not null - * @return the range object, not null - * @throws IllegalArgumentException if the element is null - * @throws ClassCastException if the element is not {@code Comparable} + * @param The type of the elements in this range. + * @param element The value to use for this range, not null. + * @return The range object, not null. + * @throws NullPointerException if the element is null. + * @throws ClassCastException if the element is not {@link Comparable}. + * @throws IllegalArgumentException if the element is a floating-point NaN. */ - public static > Range is(final T element) { - return between(element, element, null); + public static > Range is(final T element) { + return of(element, element, null); } + /** - *

    Obtains a range using the specified element as both the minimum - * and maximum in this range.

    + * Creates a range using the specified element as both the minimum + * and maximum in this range. * - *

    The range uses the specified {@code Comparator} to determine where + *

    The range uses the specified {@link Comparator} to determine where * values lie in the range.

    * - * @param the type of the elements in this range - * @param element the value to use for this range, must not be {@code null} - * @param comparator the comparator to be used, null for natural ordering - * @return the range object, not null - * @throws IllegalArgumentException if the element is null - * @throws ClassCastException if using natural ordering and the elements are not {@code Comparable} + * @param The type of the elements in this range. + * @param element The value to use for this range, must not be {@code null}. + * @param comparator The comparator to be used, null for natural ordering. + * @return The range object, not null. + * @throws NullPointerException if the element is null. + * @throws ClassCastException if using natural ordering and the elements are not {@link Comparable}. + * @throws IllegalArgumentException if the element is a floating-point NaN. */ public static Range is(final T element, final Comparator comparator) { - return between(element, element, comparator); + return of(element, element, comparator); + } + + /** + * Tests whether the element is a floating-point NaN. A NaN endpoint sorts above every value under the natural + * total order ({@link Double#compareTo(Double)} / {@link Float#compareTo(Float)}), silently producing a + * half-unbounded range whose {@code contains}/{@code fit} accept every value above the minimum. + * + * @param element The element to test, may be null. + * @return Whether the element is a floating-point NaN. + */ + private static boolean isNaN(final Object element) { + return element instanceof Double && ((Double) element).isNaN() + || element instanceof Float && ((Float) element).isNaN(); + } + + /** + * Creates a range with the specified minimum and maximum values (both inclusive). + * + *

    The range uses the natural ordering of the elements to determine where + * values lie in the range.

    + * + *

    The arguments may be passed in the order (min, max) or (max, min). + * The getMinimum and getMaximum methods will return the correct values.

    + * + * @param The type of the elements in this range. + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @return The range object, not null. + * @throws NullPointerException if either element is null. + * @throws ClassCastException if the elements are not {@link Comparable}. + * @throws IllegalArgumentException if either element is a floating-point NaN. + * @since 3.13.0 + */ + public static > Range of(final T fromInclusive, final T toInclusive) { + return of(fromInclusive, toInclusive, null); + } + + /** + * Creates a range with the specified minimum and maximum values (both inclusive). + * + *

    The range uses the specified {@link Comparator} to determine where + * values lie in the range.

    + * + *

    The arguments may be passed in the order (min, max) or (max, min). + * The getMinimum and getMaximum methods will return the correct values.

    + * + * @param The type of the elements in this range. + * @param fromInclusive The first value that defines the edge of the range, inclusive. + * @param toInclusive The second value that defines the edge of the range, inclusive. + * @param comparator The comparator to be used, null for natural ordering. + * @return The range object, not null. + * @throws NullPointerException when fromInclusive is null. + * @throws NullPointerException when toInclusive is null. + * @throws ClassCastException if using natural ordering and the elements are not {@link Comparable}. + * @throws IllegalArgumentException if either element is a floating-point NaN. + * @since 3.13.0 + */ + public static Range of(final T fromInclusive, final T toInclusive, final Comparator comparator) { + return new Range<>(fromInclusive, toInclusive, comparator); + } + + /** + * Validates that a floating-point endpoint is not NaN, mirroring the fail-closed posture of + * {@link Validate#notNaN(double, String, Object...)}. + * + * @param element The endpoint to validate. + * @param name The parameter name for the exception message. + * @throws IllegalArgumentException if the endpoint is a floating-point NaN. + */ + private static void requireNotNaN(final Object element, final String name) { + if (isNaN(element)) { + throw new IllegalArgumentException(name + " must not be NaN"); + } } /** @@ -135,7 +233,7 @@ public static Range is(final T element, final Comparator comparator) { /** * Cached output hashCode (class is immutable). */ - private transient int hashCode; + private final int hashCode; /** * The maximum value in this range (inclusive). @@ -152,22 +250,22 @@ public static Range is(final T element, final Comparator comparator) { */ private transient String toString; - // Accessors - //-------------------------------------------------------------------- - /** * Creates an instance. * - * @param element1 the first element, not null - * @param element2 the second element, not null - * @param comp the comparator to be used, null for natural ordering + * @param element1 The first element, not null. + * @param element2 The second element, not null + * @param comp The comparator to be used, null for natural ordering. + * @throws NullPointerException when element1 is null. + * @throws NullPointerException when element2 is null. + * @throws IllegalArgumentException when element1 or element2 is a floating-point NaN. */ @SuppressWarnings("unchecked") - private Range(final T element1, final T element2, final Comparator comp) { - if (element1 == null || element2 == null) { - throw new IllegalArgumentException("Elements in a range must not be null: element1=" + - element1 + ", element2=" + element2); - } + Range(final T element1, final T element2, final Comparator comp) { + Objects.requireNonNull(element1, "element1"); + Objects.requireNonNull(element2, "element2"); + requireNotNaN(element1, "element1"); + requireNotNaN(element2, "element2"); if (comp == null) { this.comparator = ComparableComparator.INSTANCE; } else { @@ -180,13 +278,14 @@ private Range(final T element1, final T element2, final Comparator comp) { this.minimum = element2; this.maximum = element1; } + this.hashCode = hash(minimum, maximum); } /** - *

    Checks whether the specified element occurs within this range.

    + * Checks whether the specified element occurs within this range. * - * @param element the element to check for, null returns false - * @return true if the specified element occurs within this range + * @param element The element to check for, null returns false. + * @return true if the specified element occurs within this range. */ public boolean contains(final T element) { if (element == null) { @@ -196,13 +295,13 @@ public boolean contains(final T element) { } /** - *

    Checks whether this range contains all the elements of the specified range.

    + * Checks whether this range contains all the elements of the specified range. * *

    This method may fail if the ranges have two different comparators or element types.

    * - * @param otherRange the range to check, null returns false - * @return true if this range contains the specified range - * @throws RuntimeException if ranges cannot be compared + * @param otherRange The range to check, null returns false. + * @return true if this range contains the specified range. + * @throws RuntimeException if ranges cannot be compared. */ public boolean containsRange(final Range otherRange) { if (otherRange == null) { @@ -213,107 +312,132 @@ public boolean containsRange(final Range otherRange) { } /** - *

    Checks where the specified element occurs relative to this range.

    + * Checks where the specified element occurs relative to this range. * *

    The API is reminiscent of the Comparable interface returning {@code -1} if * the element is before the range, {@code 0} if contained within the range and - * {@code 1} if the element is after the range.

    + * {@code 1} if the element is after the range.

    * - * @param element the element to check for, not null - * @return -1, 0 or +1 depending on the element's location relative to the range + * @param element The element to check for, not null. + * @return -1, 0 or +1 depending on the element's location relative to the range. + * @throws NullPointerException if {@code element} is {@code null}. */ public int elementCompareTo(final T element) { // Comparable API says throw NPE on null - Validate.notNull(element, "Element is null"); + Objects.requireNonNull(element, "element"); if (isAfter(element)) { return -1; - } else if (isBefore(element)) { + } + if (isBefore(element)) { return 1; - } else { - return 0; } + return 0; } - // Element tests - //-------------------------------------------------------------------- - /** - *

    Compares this range to another object to test if they are equal.

    . + * Compares this range to another object to test if they are equal. * *

    To be equal, the minimum and maximum values must be equal, which * ignores any differences in the comparator.

    * - * @param obj the reference object with which to compare - * @return true if this object is equal + * @param obj The reference object with which to compare. + * @return true if this object is equal. */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; - } else if (obj == null || obj.getClass() != getClass()) { + } + if (obj == null || obj.getClass() != getClass()) { return false; - } else { - @SuppressWarnings("unchecked") // OK because we checked the class above - final - Range range = (Range) obj; - return minimum.equals(range.minimum) && - maximum.equals(range.maximum); } + @SuppressWarnings("unchecked") // OK because we checked the class above + final + Range range = (Range) obj; + return minimum.equals(range.minimum) && + maximum.equals(range.maximum); } /** - *

    Gets the comparator being used to determine if objects are within the range.

    + * Fits the given element into this range by returning the given element or, if out of bounds, the range minimum if + * below, or the range maximum if above. + * + *
    {@code
    +     * Range range = Range.between(16, 64);
    +     * range.fit(-9) -->  16
    +     * range.fit(0)  -->  16
    +     * range.fit(15) -->  16
    +     * range.fit(16) -->  16
    +     * range.fit(17) -->  17
    +     * ...
    +     * range.fit(63) -->  63
    +     * range.fit(64) -->  64
    +     * range.fit(99) -->  64
    +     * }
    + * + * @param element The element to check for, not null. + * @return The minimum, the element, or the maximum depending on the element's location relative to the range. + * @throws NullPointerException if {@code element} is {@code null}. + * @since 3.10 + */ + public T fit(final T element) { + // Comparable API says throw NPE on null + Objects.requireNonNull(element, "element"); + if (isAfter(element)) { + return minimum; + } + if (isBefore(element)) { + return maximum; + } + return element; + } + + /** + * Gets the comparator being used to determine if objects are within the range. * *

    Natural ordering uses an internal comparator implementation, thus this * method never returns null. See {@link #isNaturalOrdering()}.

    * - * @return the comparator being used, not null + * @return The comparator being used, not null. */ public Comparator getComparator() { return comparator; } /** - *

    Gets the maximum value in this range.

    + * Gets the maximum value in this range. * - * @return the maximum value in this range, not null + * @return The maximum value in this range, not null. */ public T getMaximum() { return maximum; } /** - *

    Gets the minimum value in this range.

    + * Gets the minimum value in this range. * - * @return the minimum value in this range, not null + * @return The minimum value in this range, not null. */ public T getMinimum() { return minimum; } /** - *

    Gets a suitable hash code for the range.

    + * Gets a suitable hash code for the range. * - * @return a hash code value for this object + * @return A hash code value for this object. */ @Override public int hashCode() { - int result = hashCode; - if (hashCode == 0) { - result = 17; - result = 37 * result + getClass().hashCode(); - result = 37 * result + minimum.hashCode(); - result = 37 * result + maximum.hashCode(); - hashCode = result; - } - return result; + return hashCode; } /** * Calculate the intersection of {@code this} and an overlapping Range. - * @param other overlapping Range - * @return range representing the intersection of {@code this} and {@code other} ({@code this} if equal) - * @throws IllegalArgumentException if {@code other} does not overlap {@code this} + * + * @param other overlapping Range. + * @return range representing the intersection of {@code this} and {@code other} ({@code this} if equal). + * @throws IllegalArgumentException if {@code other} does not overlap {@code this}. * @since 3.0.1 */ public Range intersectionWith(final Range other) { @@ -326,17 +450,14 @@ public Range intersectionWith(final Range other) { } final T min = getComparator().compare(minimum, other.minimum) < 0 ? other.minimum : minimum; final T max = getComparator().compare(maximum, other.maximum) < 0 ? maximum : other.maximum; - return between(min, max, getComparator()); + return of(min, max, getComparator()); } - // Range tests - //-------------------------------------------------------------------- - /** - *

    Checks whether this range is after the specified element.

    + * Checks whether this range is after the specified element. * - * @param element the element to check for, null returns false - * @return true if this range is entirely after the specified element + * @param element The element to check for, null returns false. + * @return true if this range is entirely after the specified element. */ public boolean isAfter(final T element) { if (element == null) { @@ -346,13 +467,13 @@ public boolean isAfter(final T element) { } /** - *

    Checks whether this range is completely after the specified range.

    + * Checks whether this range is completely after the specified range. * *

    This method may fail if the ranges have two different comparators or element types.

    * - * @param otherRange the range to check, null returns false - * @return true if this range is completely after the specified range - * @throws RuntimeException if ranges cannot be compared + * @param otherRange The range to check, null returns false. + * @return true if this range is completely after the specified range. + * @throws RuntimeException if ranges cannot be compared. */ public boolean isAfterRange(final Range otherRange) { if (otherRange == null) { @@ -362,10 +483,10 @@ public boolean isAfterRange(final Range otherRange) { } /** - *

    Checks whether this range is before the specified element.

    + * Checks whether this range is before the specified element. * - * @param element the element to check for, null returns false - * @return true if this range is entirely before the specified element + * @param element The element to check for, null returns false. + * @return true if this range is entirely before the specified element. */ public boolean isBefore(final T element) { if (element == null) { @@ -375,13 +496,13 @@ public boolean isBefore(final T element) { } /** - *

    Checks whether this range is completely before the specified range.

    + * Checks whether this range is completely before the specified range. * *

    This method may fail if the ranges have two different comparators or element types.

    * - * @param otherRange the range to check, null returns false - * @return true if this range is completely before the specified range - * @throws RuntimeException if ranges cannot be compared + * @param otherRange The range to check, null returns false. + * @return true if this range is completely before the specified range. + * @throws RuntimeException if ranges cannot be compared. */ public boolean isBeforeRange(final Range otherRange) { if (otherRange == null) { @@ -391,10 +512,10 @@ public boolean isBeforeRange(final Range otherRange) { } /** - *

    Checks whether this range ends with the specified element.

    + * Checks whether this range ends with the specified element. * - * @param element the element to check for, null returns false - * @return true if the specified element occurs within this range + * @param element The element to check for, null returns false. + * @return true if the specified element occurs within this range. */ public boolean isEndedBy(final T element) { if (element == null) { @@ -403,32 +524,29 @@ public boolean isEndedBy(final T element) { return comparator.compare(element, maximum) == 0; } - // Basics - //-------------------------------------------------------------------- - /** - *

    Whether or not the Range is using the natural ordering of the elements.

    + * Tests whether or not the Range is using the natural ordering of the elements. * *

    Natural ordering uses an internal comparator implementation, thus this * method is the only way to check if a null comparator was specified.

    * - * @return true if using natural ordering + * @return true if using natural ordering. */ public boolean isNaturalOrdering() { return comparator == ComparableComparator.INSTANCE; } /** - *

    Checks whether this range is overlapped by the specified range.

    + * Tests whether this range is overlapped by the specified range. * *

    Two ranges overlap if there is at least one element in common.

    * *

    This method may fail if the ranges have two different comparators or element types.

    * - * @param otherRange the range to test, null returns false + * @param otherRange The range to test, null returns false. * @return true if the specified range overlaps with this - * range; otherwise, {@code false} - * @throws RuntimeException if ranges cannot be compared + * range; otherwise, {@code false}. + * @throws RuntimeException if ranges cannot be compared. */ public boolean isOverlappedBy(final Range otherRange) { if (otherRange == null) { @@ -440,10 +558,10 @@ public boolean isOverlappedBy(final Range otherRange) { } /** - *

    Checks whether this range starts with the specified element.

    + * Tests whether this range starts with the specified element. * - * @param element the element to check for, null returns false - * @return true if the specified element occurs within this range + * @param element The element to check for, null returns false. + * @return true if the specified element occurs within this range. */ public boolean isStartedBy(final T element) { if (element == null) { @@ -453,44 +571,40 @@ public boolean isStartedBy(final T element) { } /** - *

    - * Fits the given element into this range by returning the given element or, if out of bounds, the range minimum if - * below, or the range maximum if above. - *

    - *
    -     * Range<Integer> range = Range.between(16, 64);
    -     * range.fit(-9) -->  16
    -     * range.fit(0)  -->  16
    -     * range.fit(15) -->  16
    -     * range.fit(16) -->  16
    -     * range.fit(17) -->  17
    -     * ...
    -     * range.fit(63) -->  63
    -     * range.fit(64) -->  64
    -     * range.fit(99) -->  64
    -     * 
    - * @param element the element to check for, not null - * @return the minimum, the element, or the maximum depending on the element's location relative to the range - * @since 3.10 + * Validates the cached hashCode after deserialization. Throws a {@link InvalidObjectException} when the stored hashCode does not match the canonical hash + * of the deserialized minimum/maximum. + * + * @param in See {@link Serializable}. + * @throws IOException See {@link Serializable}. + * @throws ClassNotFoundException See {@link Serializable}. + * @throws InvalidObjectException If the hashCode doesn't match the minimum and maximum. */ - public T fit(final T element) { - // Comparable API says throw NPE on null - Validate.notNull(element, "Element is null"); - if (isAfter(element)) { - return minimum; - } else if (isBefore(element)) { - return maximum; - } else { - return element; + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + // Reject streams whose cached hashCode does not match the canonical hash of the deserialized minimum/maximum: a crafted stream cannot supply a forged + // value. + if (hashCode != hash(minimum, maximum)) { + throw new InvalidObjectException("Range hashCode does not match minimum/maximum."); + } + SerializationUtils.requireNonNull(maximum, "maximum null"); + SerializationUtils.requireNonNull(minimum, "minimum null"); + SerializationUtils.requireNonNull(comparator, "comparator null"); + // Mirror the constructor's NaN endpoint rejection: a crafted stream cannot smuggle in the degenerate + // half-unbounded range that construction refuses. + if (isNaN(minimum) || isNaN(maximum)) { + throw new InvalidObjectException("Range minimum/maximum must not be NaN."); + } + if (comparator.compare(minimum, maximum) > 0) { + throw new InvalidObjectException("Range minimum is greater than maximum under the comparator."); } } /** - *

    Gets the range as a {@code String}.

    + * Gets the range as a {@link String}. * - *

    The format of the String is '[min..max]'.

    + *

    The format of the String is '[min..max]'.

    * - * @return the {@code String} representation of this range + * @return The {@link String} representation of this range. */ @Override public String toString() { @@ -501,7 +615,7 @@ public String toString() { } /** - *

    Formats the receiver using the given format.

    + * Formats the receiver using the given format. * *

    This uses {@link java.util.Formattable} to perform the formatting. Three variables may * be used to embed the minimum, maximum and comparator. @@ -509,8 +623,8 @@ public String toString() { * and {@code %3$s} for the comparator. * The default format used by {@code toString()} is {@code [%1$s..%2$s]}.

    * - * @param format the format string, optionally containing {@code %1$s}, {@code %2$s} and {@code %3$s}, not null - * @return the formatted string, not null + * @param format The format string, optionally containing {@code %1$s}, {@code %2$s} and {@code %3$s}, not null. + * @return The formatted string, not null. */ public String toString(final String format) { return String.format(format, minimum, maximum, comparator); diff --git a/src/main/java/org/apache/commons/lang3/RegExUtils.java b/src/main/java/org/apache/commons/lang3/RegExUtils.java index c6585014766..88cc6e90a95 100644 --- a/src/main/java/org/apache/commons/lang3/RegExUtils.java +++ b/src/main/java/org/apache/commons/lang3/RegExUtils.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,17 +16,64 @@ */ package org.apache.commons.lang3; +import java.util.Objects; +import java.util.regex.Matcher; import java.util.regex.Pattern; /** - *

    Helpers to process Strings using regular expressions.

    + * Helpers to process Strings using regular expressions. + * * @see java.util.regex.Pattern * @since 3.8 */ public class RegExUtils { /** - *

    Removes each substring of the text String that matches the given regular expression pattern.

    + * The pattern to split version strings. + */ + static final Pattern VERSION_SPLIT_PATTERN = Pattern.compile("\\."); + + /** + * Compiles the given regular expression into a pattern with the {@link Pattern#DOTALL} flag. + * + * @param regex The expression to be compiled. + * @return The given regular expression compiled into a pattern with the {@link Pattern#DOTALL} flag. + * @since 3.13.0 + */ + public static Pattern dotAll(final String regex) { + return Pattern.compile(regex, Pattern.DOTALL); + } + + /** + * Compiles the given regular expression into a pattern with the {@link Pattern#DOTALL} flag, then creates a matcher that will match the given text against + * this pattern. + * + * @param regex The expression to be compiled. + * @param text The character sequence to be matched. + * @return A new matcher for this pattern. + * @since 3.18.0 + */ + public static Matcher dotAllMatcher(final String regex, final CharSequence text) { + return dotAll(regex).matcher(text); + } + + /** + * Compiles the given regular expression into a pattern with the {@link Pattern#DOTALL} flag, then creates a matcher that will match the given text against + * this pattern. + * + * @param regex The expression to be compiled. + * @param text The character sequence to be matched. + * @return A new matcher for this pattern. + * @since 3.13.0 + * @deprecated Use {@link #dotAllMatcher(String, CharSequence)}. + */ + @Deprecated + public static Matcher dotAllMatcher(final String regex, final String text) { + return dotAll(regex).matcher(text); + } + + /** + * Removes each substring of the text String that matches the given regular expression pattern. * * This method is a {@code null} safe equivalent to: *