Imported commons-lang 3.12.0 sources
This commit is contained in:
commit
3fcefd6e59
475 changed files with 181603 additions and 0 deletions
24
before/.gitattributes
vendored
Normal file
24
before/.gitattributes
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# 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.
|
||||
#
|
||||
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
*.java text diff=java
|
||||
*.html text diff=html
|
||||
*.css text
|
||||
*.js text
|
||||
*.sql text
|
25
before/.github/dependabot.yml
vendored
Normal file
25
before/.github/dependabot.yml
vendored
Normal file
|
@ -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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
48
before/.github/workflows/maven.yml
vendored
Normal file
48
before/.github/workflows/maven.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
# 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.
|
||||
|
||||
name: Java CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ 8, 11, 15 ]
|
||||
experimental: [false]
|
||||
include:
|
||||
- java: 16-ea
|
||||
experimental: true
|
||||
- java: 17-ea
|
||||
experimental: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: actions/cache@v2.1.4
|
||||
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.4.3
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- name: Build with Maven
|
||||
run: mvn -V -Ddoclint=all --file pom.xml --no-transfer-progress
|
20
before/.gitignore
vendored
Normal file
20
before/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Maven build files
|
||||
target
|
||||
*.log
|
||||
maven-eclipse.xml
|
||||
build.properties
|
||||
site-content
|
||||
*~
|
||||
|
||||
# IntelliJ IDEA files
|
||||
.idea
|
||||
.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# Eclipse files
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
.externalToolBuilders
|
||||
.checkstyle
|
36
before/.travis.yml
Normal file
36
before/.travis.yml
Normal file
|
@ -0,0 +1,36 @@
|
|||
# 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.
|
||||
|
||||
language: java
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
- openjdk15
|
||||
- openjdk-ea
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- jdk: openjdk-ea
|
||||
|
||||
script:
|
||||
- mvn -V --no-transfer-progress
|
||||
|
||||
after_success:
|
||||
- mvn -V --no-transfer-progress clean test jacoco:report coveralls:report -Ptravis-jacoco javadoc:javadoc -Ddoclint=all
|
115
before/CONTRIBUTING.md
Normal file
115
before/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
<!---
|
||||
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.
|
||||
-->
|
||||
<!---
|
||||
+======================================================================+
|
||||
|**** ****|
|
||||
|**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
|
||||
|**** DO NOT EDIT DIRECTLY ****|
|
||||
|**** ****|
|
||||
+======================================================================+
|
||||
| TEMPLATE FILE: contributing-md-template.md |
|
||||
| commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
|
||||
+======================================================================+
|
||||
| |
|
||||
| 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) |
|
||||
| |
|
||||
| 3) Example Properties |
|
||||
| |
|
||||
| <properties> |
|
||||
| <commons.jira.id>MATH</commons.jira.id> |
|
||||
| </properties> |
|
||||
| |
|
||||
+======================================================================+
|
||||
--->
|
||||
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.
|
||||
|
||||
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).
|
||||
+ 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.
|
||||
|
||||
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`.
|
||||
* 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`
|
||||
+ Respect the original code style:
|
||||
+ Only use spaces for indentation.
|
||||
+ 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Submitting Changes
|
||||
------------------
|
||||
|
||||
+ Sign and submit the Apache [Contributor License Agreement][cla] if you haven't already.
|
||||
* Note that small patches & typical bug fixes do not require a CLA as
|
||||
clause 5 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0.html#contributions)
|
||||
covers them.
|
||||
+ Push your changes to a topic branch in your fork of the repository.
|
||||
+ Submit a _Pull Request_ to the corresponding repository in the `apache` organization.
|
||||
* Verify _Files Changed_ shows only your intended changes and does not
|
||||
include additional files like `target/*.class`
|
||||
+ Update your JIRA ticket and include a link to the pull request in the ticket.
|
||||
|
||||
If you prefer to not use GitHub, then you can instead use
|
||||
`git format-patch` (or `svn diff`) and attach the patch file to the JIRA issue.
|
||||
|
||||
|
||||
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/)
|
||||
+ [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
|
115
before/Jenkinsfile
vendored
Normal file
115
before/Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
#!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 <a href="${env.BUILD_URL}">${env.BUILD_URL}</a> 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 <a href="${env.BUILD_URL}">${env.BUILD_URL}</a> 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 <a href="${env.BUILD_URL}">${env.BUILD_URL}</a> to view the results.
|
||||
""",
|
||||
to: "notifications@commons.apache.org",
|
||||
recipientProviders: [[$class: 'DevelopersRecipientProvider']]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
202
before/LICENSE.txt
Normal file
202
before/LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
5
before/NOTICE.txt
Normal file
5
before/NOTICE.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
Apache Commons Lang
|
||||
Copyright 2001-2021 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (https://www.apache.org/).
|
108
before/README.md
Normal file
108
before/README.md
Normal file
|
@ -0,0 +1,108 @@
|
|||
<!---
|
||||
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.
|
||||
-->
|
||||
<!---
|
||||
+======================================================================+
|
||||
|**** ****|
|
||||
|**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
|
||||
|**** DO NOT EDIT DIRECTLY ****|
|
||||
|**** ****|
|
||||
+======================================================================+
|
||||
| TEMPLATE FILE: readme-md-template.md |
|
||||
| commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
|
||||
+======================================================================+
|
||||
| |
|
||||
| 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) |
|
||||
| - commons.release.version (required) |
|
||||
| |
|
||||
| 3) Example Properties |
|
||||
| |
|
||||
| <properties> |
|
||||
| <commons.componentid>math</commons.componentid> |
|
||||
| <commons.release.version>1.2</commons.release.version> |
|
||||
| </properties> |
|
||||
| |
|
||||
+======================================================================+
|
||||
--->
|
||||
Apache Commons Lang
|
||||
===================
|
||||
|
||||
[![Travis-CI Status](https://travis-ci.org/apache/commons-lang.svg)](https://travis-ci.org/apache/commons-lang)
|
||||
[![GitHub Actions Status](https://github.com/apache/commons-lang/workflows/Java%20CI/badge.svg)](https://github.com/apache/commons-lang/actions)
|
||||
[![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.12.0.svg)](https://javadoc.io/doc/org.apache.commons/commons-lang3/3.12.0)
|
||||
|
||||
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.
|
||||
|
||||
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/apidocs) can be browsed.
|
||||
Questions related to the usage of Apache Commons Lang should be posted to the [user mailing list][ml].
|
||||
|
||||
Where can I get 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:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
We accept Pull Requests via GitHub. The [developer mailing list][ml] 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.
|
||||
+ 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```.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
+ [Apache Commons Homepage](https://commons.apache.org/)
|
||||
+ [Apache Issue Tracker (JIRA)](https://issues.apache.org/jira/browse/LANG)
|
||||
+ [Apache Commons Twitter Account](https://twitter.com/ApacheCommons)
|
||||
+ `#apache-commons` IRC channel on `irc.freenode.org`
|
||||
|
||||
[ml]:https://commons.apache.org/mail-lists.html
|
1610
before/RELEASE-NOTES.txt
Normal file
1610
before/RELEASE-NOTES.txt
Normal file
File diff suppressed because it is too large
Load diff
17
before/SECURITY.md
Normal file
17
before/SECURITY.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!---
|
||||
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.
|
||||
-->
|
||||
The Apache Commons security page is [https://commons.apache.org/security.html](https://commons.apache.org/security.html).
|
1008
before/pom.xml
Normal file
1008
before/pom.xml
Normal file
File diff suppressed because it is too large
Load diff
164
before/spotbugs-exclude-filter.xml
Normal file
164
before/spotbugs-exclude-filter.xml
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<!--
|
||||
This file contains some false positive bugs detected by findbugs. Their
|
||||
false positive nature has been analyzed individually and they have been
|
||||
put here to instruct findbugs it must ignore them.
|
||||
-->
|
||||
<FindBugsFilter>
|
||||
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.ArrayUtils" />
|
||||
<Method name="addFirst" />
|
||||
<Bug pattern="NP_LOAD_OF_KNOWN_NULL_VALUE" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: Optimization to use == -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.BooleanUtils" />
|
||||
<Or>
|
||||
<Method name="toBoolean" />
|
||||
<Method name="toBooleanObject" />
|
||||
</Or>
|
||||
<Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ" />
|
||||
</Match>
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.BooleanUtils" />
|
||||
<Method name="toBoolean" />
|
||||
<Bug pattern="RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: Behavior documented in javadoc -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.BooleanUtils" />
|
||||
<Or>
|
||||
<Method name="negate" />
|
||||
<Method name="toBooleanObject" />
|
||||
</Or>
|
||||
<Bug pattern="NP_BOOLEAN_RETURN_NULL" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: base class cannot be changed and field is properly checked against null so behavior is OK -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.text.ExtendedMessageFormat" />
|
||||
<Method name="applyPattern" />
|
||||
<Bug pattern="UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: Optimization to use == -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.StringUtils" />
|
||||
<Or>
|
||||
<Method name="indexOfDifference"/>
|
||||
<Method name="compare" params="java.lang.String,java.lang.String,boolean"/>
|
||||
<Method name="compareIgnoreCase" params="java.lang.String,java.lang.String,boolean"/>
|
||||
</Or>
|
||||
<Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: Very much intended to do a fall through on the switch -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.math.NumberUtils" />
|
||||
<Method name="createNumber"/>
|
||||
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: Very much intended to do a fall through on the switch -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.time.DateUtils" />
|
||||
<Method name="getFragment"/>
|
||||
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: toProperString is lazily loaded -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.math.Fraction" />
|
||||
<Field name="toProperString" />
|
||||
<Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: It does call super.clone(), but via a subsequent method -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.text.StrTokenizer" />
|
||||
<Method name="clone"/>
|
||||
<Bug pattern="CN_IDIOM_NO_SUPER_CALL" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: FindBugs 2.0.2 used in maven-findbugs-plugin 2.5.2 seems to have problems with detection of default cases
|
||||
in switch statements. All the excluded methods have switch statements that conatin a default case. -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.math.NumberUtils"/>
|
||||
<Method name="createNumber" />
|
||||
<Bug pattern="SF_SWITCH_NO_DEFAULT" />
|
||||
</Match>
|
||||
<!-- Reason: FindBugs does not correctly recognize default branches in switch statements without break statements.
|
||||
See, e.g., the report at https://sourceforge.net/p/findbugs/bugs/1298 -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.time.FastDateParser"/>
|
||||
<Or>
|
||||
<Method name="getStrategy" />
|
||||
<Method name="simpleQuote" params="java.lang.StringBuilder, java.lang.String"/>
|
||||
</Or>
|
||||
<Bug pattern="SF_SWITCH_NO_DEFAULT" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: FindBugs cannot correctly recognize default branches in switch statements without break statements.
|
||||
See, e.g., the report at https://sourceforge.net/p/findbugs/bugs/1298 -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.time.FastDatePrinter"/>
|
||||
<Method name="appendFullDigits" params="java.lang.Appendable, int, int"/>
|
||||
<Bug pattern="SF_SWITCH_NO_DEFAULT" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: The fallthrough on the swich statement is intentional -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.time.FastDatePrinter"/>
|
||||
<Method name="appendFullDigits" params="java.lang.Appendable, int, int"/>
|
||||
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: Internal class that is used only as a key for an internal FormatCache. For this reason we can
|
||||
be sure, that equals will never be called with null or types other than MultipartKey.
|
||||
-->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.time.FormatCache$MultipartKey" />
|
||||
<Method name="equals" />
|
||||
<Bug pattern="BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS" />
|
||||
</Match>
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.time.FormatCache$MultipartKey" />
|
||||
<Method name="equals" />
|
||||
<Bug pattern="NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: toString() can return null! -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.compare.ObjectToStringComparator" />
|
||||
<Method name="compare" />
|
||||
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" />
|
||||
</Match>
|
||||
|
||||
<!-- Reason: requireNonNull is supposed to take a nullable parameter,
|
||||
whatever Spotbugs thinks of it. -->
|
||||
<Match>
|
||||
<Class name="org.apache.commons.lang3.function.Objects" />
|
||||
<Method name="requireNonNull" />
|
||||
<Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" />
|
||||
</Match>
|
||||
</FindBugsFilter>
|
46
before/src/assembly/bin.xml
Normal file
46
before/src/assembly/bin.xml
Normal file
|
@ -0,0 +1,46 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<assembly>
|
||||
<id>bin</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeSiteDirectory>false</includeSiteDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<includes>
|
||||
<include>LICENSE.txt</include>
|
||||
<include>NOTICE.txt</include>
|
||||
<include>RELEASE-NOTES.txt</include>
|
||||
<include>README.md</include>
|
||||
<include>CONTRIBUTING.md</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target/site/apidocs</directory>
|
||||
<outputDirectory>apidocs</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
44
before/src/assembly/src.xml
Normal file
44
before/src/assembly/src.xml
Normal file
|
@ -0,0 +1,44 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<assembly>
|
||||
<id>src</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<baseDirectory>${project.artifactId}-${commons.release.version}-src</baseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<includes>
|
||||
<include>.travis.yml</include>
|
||||
<include>checkstyle.xml</include>
|
||||
<include>checkstyle-suppressions.xml</include>
|
||||
<include>spotbugs-exclude-filter.xml</include>
|
||||
<include>LICENSE.txt</include>
|
||||
<include>NOTICE.txt</include>
|
||||
<include>pom.xml</include>
|
||||
<include>PROPOSAL.html</include>
|
||||
<include>RELEASE-NOTES.txt</include>
|
||||
<include>README.md</include>
|
||||
<include>CONTRIBUTING.md</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>src</directory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
1223
before/src/changes/changes.xml
Normal file
1223
before/src/changes/changes.xml
Normal file
File diff suppressed because it is too large
Load diff
144
before/src/changes/release-notes.vm
Normal file
144
before/src/changes/release-notes.vm
Normal file
|
@ -0,0 +1,144 @@
|
|||
## 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.
|
||||
##
|
||||
|
||||
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.
|
||||
|
||||
|
||||
${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
|
||||
|
||||
$introduction.replaceAll("(?<!\015)\012", "
|
||||
").replaceAll("(?m)^ +","")
|
||||
|
||||
## N.B. the available variables are described here:
|
||||
## http://maven.apache.org/plugins/maven-changes-plugin/examples/using-a-custom-announcement-template.html
|
||||
##
|
||||
## Hack to improve layout: replace all pairs of spaces with a single new-line
|
||||
$release.description.replaceAll(" ", "
|
||||
")
|
||||
|
||||
## set up indent sizes. Only change indent1
|
||||
#set($props=${project.properties})
|
||||
#set($jiralen=$props.get("commons.jira.id").length())
|
||||
## indent1 = POOL-nnnn:
|
||||
#set($blanklen=$jiralen+6)## +6 for "-nnnn:"
|
||||
## must be at least as long as the longest JIRA id
|
||||
#set($blanks=" ")
|
||||
#set($indent1=$blanks.substring(0,$blanklen))
|
||||
## indent2 allows for issue wrapper
|
||||
#set($indent2="$indent1 ")
|
||||
##
|
||||
#macro ( processaction )
|
||||
## Use replaceAll to fix up LF-only line ends on Windows.
|
||||
#set($action=$actionItem.getAction().replaceAll("\n","
|
||||
"))
|
||||
## Fix up indentation for multi-line action descriptions
|
||||
#set($action=$action.replaceAll("(?m)^ +",$indent2))
|
||||
#if ($actionItem.getIssue())
|
||||
#set($issue="$actionItem.getIssue():")
|
||||
## Pad shorter issue numbers
|
||||
#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
|
||||
#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
|
||||
#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
|
||||
#else
|
||||
#set($issue=$indent1)
|
||||
#end
|
||||
#if ($actionItem.getDueTo())
|
||||
#set($dueto=" Thanks to $actionItem.getDueTo().")
|
||||
#else
|
||||
#set($dueto="")
|
||||
#end
|
||||
o $issue ${action}$dueto
|
||||
#set($action="")
|
||||
#set($issue="")
|
||||
#set($dueto="")
|
||||
#end
|
||||
##
|
||||
#if ($release.getActions().size() == 0)
|
||||
No changes defined in this version.
|
||||
#else
|
||||
Changes in this version include:
|
||||
|
||||
#if ($release.getActions('add').size() !=0)
|
||||
New features:
|
||||
#foreach($actionItem in $release.getActions('add'))
|
||||
#processaction()
|
||||
#end
|
||||
#end
|
||||
|
||||
#if ($release.getActions('fix').size() !=0)
|
||||
Fixed Bugs:
|
||||
#foreach($actionItem in $release.getActions('fix'))
|
||||
#processaction()
|
||||
#end
|
||||
#end
|
||||
|
||||
#if ($release.getActions('update').size() !=0)
|
||||
Changes:
|
||||
#foreach($actionItem in $release.getActions('update'))
|
||||
#processaction()
|
||||
#end
|
||||
#end
|
||||
|
||||
#if ($release.getActions('remove').size() !=0)
|
||||
Removed:
|
||||
#foreach($actionItem in $release.getActions('remove'))
|
||||
#processaction()
|
||||
#end
|
||||
#end
|
||||
## End of main loop
|
||||
#end
|
||||
|
||||
Historical list of changes: ${project.url}changes-report.html
|
||||
|
||||
For complete information on ${project.name}, including instructions on how to submit bug reports,
|
||||
patches, or suggestions for improvement, see the Apache ${project.name} website:
|
||||
|
||||
${project.url}
|
||||
|
||||
Download page: ${project.url}download_lang.cgi
|
||||
|
||||
Have fun!
|
||||
-Apache Commons Team
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
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.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
/**
|
||||
* <p>Helper methods for working with {@link Annotation} instances.</p>
|
||||
*
|
||||
* <p>This class contains various utility methods that make working with
|
||||
* annotations simpler.</p>
|
||||
*
|
||||
* <p>{@link Annotation} instances are always proxy objects; unfortunately
|
||||
* dynamic proxies cannot be depended upon to know how to implement certain
|
||||
* methods in the same manner as would be done by "natural" {@link Annotation}s.
|
||||
* The methods presented in this class can be used to avoid that possibility. It
|
||||
* is of course also possible for dynamic proxies to actually delegate their
|
||||
* e.g. {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/
|
||||
* {@link Annotation#toString()} implementations to {@link AnnotationUtils}.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
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;
|
||||
|
||||
{
|
||||
setDefaultFullDetail(true);
|
||||
setArrayContentDetail(true);
|
||||
setUseClassName(true);
|
||||
setUseShortClassName(true);
|
||||
setUseIdentityHashCode(false);
|
||||
setContentStart("(");
|
||||
setContentEnd(")");
|
||||
setFieldSeparator(", ");
|
||||
setArrayStart("[");
|
||||
setArrayEnd("]");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected String getShortClassName(final Class<?> cls) {
|
||||
for (final Class<?> iface : ClassUtils.getAllInterfaces(cls)) {
|
||||
if (Annotation.class.isAssignableFrom(iface)) {
|
||||
return "@" + iface.getName();
|
||||
}
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* <p>{@code AnnotationUtils} instances should NOT be constructed in
|
||||
* standard programming. Instead, the class should be used statically.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public AnnotationUtils() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Checks if two annotations are equal using the criteria for equality
|
||||
* presented in the {@link Annotation#equals(Object)} API docs.</p>
|
||||
*
|
||||
* @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
|
||||
* {@code false} unless both are {@code null}
|
||||
* @return {@code true} if the two annotations are {@code equal} or both
|
||||
* {@code null}
|
||||
*/
|
||||
public static boolean equals(final Annotation a1, final Annotation a2) {
|
||||
if (a1 == a2) {
|
||||
return true;
|
||||
}
|
||||
if (a1 == null || a2 == null) {
|
||||
return false;
|
||||
}
|
||||
final Class<? extends Annotation> type1 = a1.annotationType();
|
||||
final Class<? extends Annotation> type2 = a2.annotationType();
|
||||
Validate.notNull(type1, "Annotation %s with null annotationType()", a1);
|
||||
Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
|
||||
if (!type1.equals(type2)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
for (final Method m : type1.getDeclaredMethods()) {
|
||||
if (m.getParameterTypes().length == 0
|
||||
&& isValidAnnotationMemberType(m.getReturnType())) {
|
||||
final Object v1 = m.invoke(a1);
|
||||
final Object v2 = m.invoke(a2);
|
||||
if (!memberEquals(m.getReturnType(), v1, v2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final IllegalAccessException | InvocationTargetException ex) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Generate a hash code for the given annotation using the algorithm
|
||||
* presented in the {@link Annotation#hashCode()} API docs.</p>
|
||||
*
|
||||
* @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
|
||||
* annotation member access
|
||||
* @throws IllegalStateException if an annotation method invocation returns
|
||||
* {@code null}
|
||||
*/
|
||||
public static int hashCode(final Annotation a) {
|
||||
int result = 0;
|
||||
final Class<? extends Annotation> type = a.annotationType();
|
||||
for (final Method m : type.getDeclaredMethods()) {
|
||||
try {
|
||||
final Object value = m.invoke(a);
|
||||
if (value == null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Generate a string representation of an Annotation, as suggested by
|
||||
* {@link Annotation#toString()}.</p>
|
||||
*
|
||||
* @param a the annotation of which a string representation is desired
|
||||
* @return the standard string representation of an annotation, not
|
||||
* {@code null}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if the specified type is permitted as an annotation member.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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) {
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
return type.isPrimitive() || type.isEnum() || type.isAnnotation()
|
||||
|| 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
|
||||
*/
|
||||
private static boolean memberEquals(final Class<?> type, final Object o1, final Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
if (o1 == null || o2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (type.isArray()) {
|
||||
return arrayMemberEquals(type.getComponentType(), o1, o2);
|
||||
}
|
||||
if (type.isAnnotation()) {
|
||||
return equals((Annotation) o1, (Annotation) o2);
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param a1 the first array
|
||||
* @param a2 the second array
|
||||
* @return a flag whether these arrays are equal
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
132
before/src/main/java/org/apache/commons/lang3/ArchUtils.java
Normal file
132
before/src/main/java/org/apache/commons/lang3/ArchUtils.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.arch.Processor;
|
||||
|
||||
/**
|
||||
* An utility class for the os.arch System Property. The class defines methods for
|
||||
* identifying the architecture of the current JVM.
|
||||
* <p>
|
||||
* Important: The os.arch System Property returns the architecture used by the JVM
|
||||
* not of the operating system.
|
||||
* </p>
|
||||
* @since 3.6
|
||||
*/
|
||||
public class ArchUtils {
|
||||
|
||||
private static final Map<String, Processor> ARCH_TO_PROCESSOR;
|
||||
|
||||
static {
|
||||
ARCH_TO_PROCESSOR = new HashMap<>();
|
||||
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 processor The {@link Processor} to add.
|
||||
* @throws IllegalStateException If the key already exists.
|
||||
*/
|
||||
private static void addProcessor(final String key, final Processor processor) {
|
||||
if (ARCH_TO_PROCESSOR.containsKey(key)) {
|
||||
throw new IllegalStateException("Key " + key + " already exists in processor map");
|
||||
}
|
||||
ARCH_TO_PROCESSOR.put(key, processor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given {@link Processor} with the given keys to the map.
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Processor} object of the current JVM.
|
||||
*
|
||||
* <p>
|
||||
* Important: The os.arch System Property returns the architecture used by the JVM
|
||||
* not of the operating system.
|
||||
* </p>
|
||||
*
|
||||
* @return A {@link Processor} when supported, else {@code null}.
|
||||
*/
|
||||
public static Processor getProcessor() {
|
||||
return getProcessor(SystemUtils.OS_ARCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param value A {@link String} like a value returned by the 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);
|
||||
}
|
||||
|
||||
}
|
141
before/src/main/java/org/apache/commons/lang3/ArraySorter.java
Normal file
141
before/src/main/java/org/apache/commons/lang3/ArraySorter.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Sorts and returns arrays in the fluent style.
|
||||
*
|
||||
* @since 3.12.0
|
||||
*/
|
||||
public class ArraySorter {
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(byte[])
|
||||
*/
|
||||
public static byte[] sort(final byte[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(char[])
|
||||
*/
|
||||
public static char[] sort(final char[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(double[])
|
||||
*/
|
||||
public static double[] sort(final double[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(float[])
|
||||
*/
|
||||
public static float[] sort(final float[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(int[])
|
||||
*/
|
||||
public static int[] sort(final int[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(long[])
|
||||
*/
|
||||
public static long[] sort(final long[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(short[])
|
||||
*/
|
||||
public static short[] sort(final short[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param <T> the array type.
|
||||
* @param array the array to sort.
|
||||
* @return the given array.
|
||||
* @see Arrays#sort(Object[])
|
||||
*/
|
||||
public static <T> T[] sort(final T[] array) {
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and returns the given array.
|
||||
*
|
||||
* @param <T> the array type.
|
||||
* @param array the array to sort.
|
||||
* @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> T[] sort(final T[] array, final Comparator<? super T> comparator) {
|
||||
Arrays.sort(array, comparator);
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
9654
before/src/main/java/org/apache/commons/lang3/ArrayUtils.java
Normal file
9654
before/src/main/java/org/apache/commons/lang3/ArrayUtils.java
Normal file
File diff suppressed because it is too large
Load diff
322
before/src/main/java/org/apache/commons/lang3/BitField.java
Normal file
322
before/src/main/java/org/apache/commons/lang3/BitField.java
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>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}.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>As an example, consider a car painting machine that accepts
|
||||
* paint instructions as integers. Bit fields can be used to encode this:</p>
|
||||
*
|
||||
*<pre>
|
||||
* // 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);
|
||||
*
|
||||
* // 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);
|
||||
*</pre>
|
||||
*
|
||||
* <p>Using these {@code BitField} instances, a paint instruction can be
|
||||
* encoded into an integer:</p>
|
||||
*
|
||||
*<pre>
|
||||
* int paintInstruction = 0;
|
||||
* paintInstruction = red.setValue(paintInstruction, 35);
|
||||
* paintInstruction = green.setValue(paintInstruction, 100);
|
||||
* paintInstruction = blue.setValue(paintInstruction, 255);
|
||||
*</pre>
|
||||
*
|
||||
* <p>Flags and data can be retrieved from the integer:</p>
|
||||
*
|
||||
*<pre>
|
||||
* // 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
|
||||
*</pre>
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class BitField {
|
||||
|
||||
private final int _mask;
|
||||
private final int _shift_count;
|
||||
|
||||
/**
|
||||
* <p>Creates a BitField instance.</p>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the value for the specified BitField, appropriately
|
||||
* shifted right.</p>
|
||||
*
|
||||
* <p>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).</p>
|
||||
*
|
||||
* @see #setValue(int,int)
|
||||
* @param holder the int data containing the bits we're interested
|
||||
* in
|
||||
* @return the selected bits, shifted right appropriately
|
||||
*/
|
||||
public int getValue(final int holder) {
|
||||
return getRawValue(holder) >> _shift_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the value for the specified BitField, appropriately
|
||||
* shifted right, as a short.</p>
|
||||
*
|
||||
* <p>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).</p>
|
||||
*
|
||||
* @see #setShortValue(short,short)
|
||||
* @param holder the short data containing the bits we're
|
||||
* interested in
|
||||
* @return the selected bits, shifted right appropriately
|
||||
*/
|
||||
public short getShortValue(final short holder) {
|
||||
return (short) getValue(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the value for the specified BitField, unshifted.</p>
|
||||
*
|
||||
* @param holder the int data containing the bits we're
|
||||
* interested in
|
||||
* @return the selected bits
|
||||
*/
|
||||
public int getRawValue(final int holder) {
|
||||
return holder & _mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the value for the specified BitField, unshifted.</p>
|
||||
*
|
||||
* @param holder the short data containing the bits we're
|
||||
* interested in
|
||||
* @return the selected bits
|
||||
*/
|
||||
public short getShortRawValue(final short holder) {
|
||||
return (short) getRawValue(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns whether the field is set or not.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
public boolean isSet(final int holder) {
|
||||
return (holder & _mask) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns whether all of the bits are set or not.</p>
|
||||
*
|
||||
* <p>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}.</p>
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
public boolean isAllSet(final int holder) {
|
||||
return (holder & _mask) == _mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces the bits with new values.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public int setValue(final int holder, final int value) {
|
||||
return (holder & ~_mask) | ((value << _shift_count) & _mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces the bits with new values.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public short setShortValue(final short holder, final short value) {
|
||||
return (short) setValue(holder, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clears the bits.</p>
|
||||
*
|
||||
* @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})
|
||||
*/
|
||||
public int clear(final int holder) {
|
||||
return holder & ~_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clears the bits.</p>
|
||||
*
|
||||
* @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})
|
||||
*/
|
||||
public short clearShort(final short holder) {
|
||||
return (short) clear(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clears the bits.</p>
|
||||
*
|
||||
* @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})
|
||||
*/
|
||||
public byte clearByte(final byte holder) {
|
||||
return (byte) clear(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the bits.</p>
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
public int set(final int holder) {
|
||||
return holder | _mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the bits.</p>
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
public short setShort(final short holder) {
|
||||
return (short) set(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the bits.</p>
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
public byte setByte(final byte holder) {
|
||||
return (byte) set(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a boolean BitField.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public int setBoolean(final int holder, final boolean flag) {
|
||||
return flag ? set(holder) : clear(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a boolean BitField.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public short setShortBoolean(final short holder, final boolean flag) {
|
||||
return flag ? setShort(holder) : clearShort(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a boolean BitField.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public byte setByteBoolean(final byte holder, final boolean flag) {
|
||||
return flag ? setByte(holder) : clearByte(holder);
|
||||
}
|
||||
|
||||
}
|
1139
before/src/main/java/org/apache/commons/lang3/BooleanUtils.java
Normal file
1139
before/src/main/java/org/apache/commons/lang3/BooleanUtils.java
Normal file
File diff suppressed because it is too large
Load diff
110
before/src/main/java/org/apache/commons/lang3/CharEncoding.java
Normal file
110
before/src/main/java/org/apache/commons/lang3/CharEncoding.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.IllegalCharsetNameException;
|
||||
|
||||
/**
|
||||
* <p>Character encoding names required of every implementation of the Java platform.</p>
|
||||
*
|
||||
* <p>According to <a href="http://docs.oracle.com/javase/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
|
||||
* encoding names</a>:</p>
|
||||
*
|
||||
* <p><cite>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.
|
||||
* </cite></p>
|
||||
*
|
||||
* @see <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html">JRE character encoding names</a>
|
||||
* @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.
|
||||
* This class will be removed in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public class CharEncoding {
|
||||
|
||||
/**
|
||||
* <p>ISO Latin Alphabet #1, also known as ISO-LATIN-1.</p>
|
||||
*
|
||||
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||
*/
|
||||
public static final String ISO_8859_1 = "ISO-8859-1";
|
||||
|
||||
/**
|
||||
* <p>Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block
|
||||
* of the Unicode character set.</p>
|
||||
*
|
||||
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||
*/
|
||||
public static final String US_ASCII = "US-ASCII";
|
||||
|
||||
/**
|
||||
* <p>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).</p>
|
||||
*
|
||||
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||
*/
|
||||
public static final String UTF_16 = "UTF-16";
|
||||
|
||||
/**
|
||||
* <p>Sixteen-bit Unicode Transformation Format, big-endian byte order.</p>
|
||||
*
|
||||
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||
*/
|
||||
public static final String UTF_16BE = "UTF-16BE";
|
||||
|
||||
/**
|
||||
* <p>Sixteen-bit Unicode Transformation Format, little-endian byte order.</p>
|
||||
*
|
||||
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||
*/
|
||||
public static final String UTF_16LE = "UTF-16LE";
|
||||
|
||||
/**
|
||||
* <p>Eight-bit Unicode Transformation Format.</p>
|
||||
*
|
||||
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||
*/
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* <p>Returns whether the named charset is supported.</p>
|
||||
*
|
||||
* <p>This is similar to <a
|
||||
* href="http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html#isSupported%28java.lang.String%29">
|
||||
* java.nio.charset.Charset.isSupported(String)</a> but handles more formats</p>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isSupported(final String name) {
|
||||
if (name == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return Charset.isSupported(name);
|
||||
} catch (final IllegalCharsetNameException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
367
before/src/main/java/org/apache/commons/lang3/CharRange.java
Normal file
367
before/src/main/java/org/apache/commons/lang3/CharRange.java
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* <p>A contiguous range of characters, optionally negated.</p>
|
||||
*
|
||||
* <p>Instances are immutable.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 1.0
|
||||
*/
|
||||
// TODO: This is no longer public and will be removed later as CharSet is moved
|
||||
// to depend on Range.
|
||||
final class CharRange implements Iterable<Character>, Serializable {
|
||||
|
||||
/**
|
||||
* Required for serialization support. Lang version 2.0.
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 8270183163158333422L;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Cached toString. */
|
||||
private transient String iToString;
|
||||
|
||||
/** Empty array. */
|
||||
static final CharRange[] EMPTY_ARRAY = new CharRange[0];
|
||||
|
||||
/**
|
||||
* <p>Constructs a {@code CharRange} over a set of characters,
|
||||
* optionally negating the range.</p>
|
||||
*
|
||||
* <p>A negated range includes everything except that defined by the
|
||||
* start and end characters.</p>
|
||||
*
|
||||
* <p>If start and end are in the wrong order, they are reversed.
|
||||
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a {@code CharRange} over a single character.</p>
|
||||
*
|
||||
* @param ch only character in this range
|
||||
* @return the new CharRange object
|
||||
* @since 2.5
|
||||
*/
|
||||
public static CharRange is(final char ch) {
|
||||
return new CharRange(ch, ch, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a negated {@code CharRange} over a single character.</p>
|
||||
*
|
||||
* <p>A negated range includes everything except that defined by the
|
||||
* single character.</p>
|
||||
*
|
||||
* @param ch only character in this range
|
||||
* @return the new CharRange object
|
||||
* @since 2.5
|
||||
*/
|
||||
public static CharRange isNot(final char ch) {
|
||||
return new CharRange(ch, ch, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a {@code CharRange} over a set of characters.</p>
|
||||
*
|
||||
* <p>If start and end are in the wrong order, they are reversed.
|
||||
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||
*
|
||||
* @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 isIn(final char start, final char end) {
|
||||
return new CharRange(start, end, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a negated {@code CharRange} over a set of characters.</p>
|
||||
*
|
||||
* <p>A negated range includes everything except that defined by the
|
||||
* start and end characters.</p>
|
||||
*
|
||||
* <p>If start and end are in the wrong order, they are reversed.
|
||||
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||
*
|
||||
* @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
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Gets the start character for this character range.</p>
|
||||
*
|
||||
* @return the start char (inclusive)
|
||||
*/
|
||||
public char getStart() {
|
||||
return this.start;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the end character for this character range.</p>
|
||||
*
|
||||
* @return the end char (inclusive)
|
||||
*/
|
||||
public char getEnd() {
|
||||
return this.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Is this {@code CharRange} negated.</p>
|
||||
*
|
||||
* <p>A negated range includes everything except that defined by the
|
||||
* start and end characters.</p>
|
||||
*
|
||||
* @return {@code true} if negated
|
||||
*/
|
||||
public boolean isNegated() {
|
||||
return negated;
|
||||
}
|
||||
|
||||
// Contains
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Is the character specified contained in this range.</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Are all the characters of the passed in range contained in
|
||||
* this range.</p>
|
||||
*
|
||||
* @param range the range to check against
|
||||
* @return {@code true} if this range entirely contains the input range
|
||||
* @throws IllegalArgumentException if {@code null} input
|
||||
*/
|
||||
public boolean contains(final CharRange range) {
|
||||
Validate.notNull(range, "range");
|
||||
if (negated) {
|
||||
if (range.negated) {
|
||||
return start >= range.start && end <= range.end;
|
||||
}
|
||||
return range.end < start || range.start > end;
|
||||
}
|
||||
if (range.negated) {
|
||||
return start == 0 && end == Character.MAX_VALUE;
|
||||
}
|
||||
return start <= range.start && end >= range.end;
|
||||
}
|
||||
|
||||
// Basics
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Compares two CharRange objects, returning true if they represent
|
||||
* exactly the same range of characters defined in the same way.</p>
|
||||
*
|
||||
* @param obj the object to compare to
|
||||
* @return true if equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CharRange)) {
|
||||
return false;
|
||||
}
|
||||
final CharRange other = (CharRange) obj;
|
||||
return start == other.start && end == other.end && negated == other.negated;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a hashCode compatible with the equals method.</p>
|
||||
*
|
||||
* @return a suitable hashCode
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 83 + start + 7 * end + (negated ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a string representation of the character range.</p>
|
||||
*
|
||||
* @return string representation of this range
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
// Expansions
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Returns an iterator which can be used to walk through the characters described by this range.</p>
|
||||
*
|
||||
* <p>#NotThreadSafe# the iterator is not thread-safe</p>
|
||||
* @return an iterator to the chars represented by this range
|
||||
* @since 2.5
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Character> iterator() {
|
||||
return new CharacterIterator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Character {@link Iterator}.
|
||||
* <p>#NotThreadSafe#</p>
|
||||
*/
|
||||
private static class CharacterIterator implements Iterator<Character> {
|
||||
/** The current character */
|
||||
private char current;
|
||||
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character in the iteration
|
||||
*
|
||||
* @return {@code Character} for the next character
|
||||
*/
|
||||
@Override
|
||||
public Character next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final char cur = current;
|
||||
prepareNext();
|
||||
return Character.valueOf(cur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always throws UnsupportedOperationException.
|
||||
*
|
||||
* @throws UnsupportedOperationException Always thrown.
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>Operations on {@link CharSequence} that are
|
||||
* {@code null} safe.</p>
|
||||
*
|
||||
* @see CharSequence
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CharSequenceUtils {
|
||||
|
||||
private static final int NOT_FOUND = -1;
|
||||
|
||||
/**
|
||||
* <p>{@code CharSequenceUtils} instances should NOT be constructed in
|
||||
* standard programming. </p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public CharSequenceUtils() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Returns a new {@code CharSequence} that is a subsequence of this
|
||||
* sequence starting with the {@code char} value at the specified index.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Returns the index within {@code cs} of the first occurrence of the
|
||||
* specified character, starting the search at the specified index.
|
||||
* <p>
|
||||
* 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 <i>k</i> such that:
|
||||
* <blockquote><pre>
|
||||
* (this.charAt(<i>k</i>) == searchChar) && (<i>k</i> >= start)
|
||||
* </pre></blockquote>
|
||||
* is true. For other values of {@code searchChar}, it is the
|
||||
* smallest value <i>k</i> such that:
|
||||
* <blockquote><pre>
|
||||
* (this.codePointAt(<i>k</i>) == searchChar) && (<i>k</i> >= start)
|
||||
* </pre></blockquote>
|
||||
* is true. In either case, if no such character occurs inm {@code cs}
|
||||
* at or after position {@code start}, then
|
||||
* {@code -1} is returned.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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}
|
||||
*/
|
||||
static int indexOf(final CharSequence cs, final int searchChar, int start) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).indexOf(searchChar, start);
|
||||
}
|
||||
final int sz = cs.length();
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||
for (int i = start; i < sz; i++) {
|
||||
if (cs.charAt(i) == searchChar) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
//supplementary characters (LANG1300)
|
||||
if (searchChar <= Character.MAX_CODE_POINT) {
|
||||
final char[] chars = Character.toChars(searchChar);
|
||||
for (int i = start; i < sz - 1; i++) {
|
||||
final char high = cs.charAt(i);
|
||||
final char low = cs.charAt(i + 1);
|
||||
if (high == chars[0] && low == chars[1]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
|
||||
*
|
||||
* @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 indexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).indexOf(searchChar.toString(), start);
|
||||
} else if (cs instanceof StringBuilder) {
|
||||
return ((StringBuilder) cs).indexOf(searchChar.toString(), start);
|
||||
} else if (cs instanceof StringBuffer) {
|
||||
return ((StringBuffer) cs).indexOf(searchChar.toString(), 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);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <i>k</i> such that:
|
||||
* <blockquote><pre>
|
||||
* (this.charAt(<i>k</i>) == searchChar) && (<i>k</i> <= start)
|
||||
* </pre></blockquote>
|
||||
* is true. For other values of {@code searchChar}, it is the
|
||||
* largest value <i>k</i> such that:
|
||||
* <blockquote><pre>
|
||||
* (this.codePointAt(<i>k</i>) == searchChar) && (<i>k</i> <= start)
|
||||
* </pre></blockquote>
|
||||
* is true. In either case, if no such character occurs in {@code cs}
|
||||
* at or before position {@code start}, then {@code -1} is returned.
|
||||
*
|
||||
* <p>All indices are specified in {@code char} values
|
||||
* (Unicode code units).
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
static int lastIndexOf(final CharSequence cs, final int searchChar, int start) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).lastIndexOf(searchChar, start);
|
||||
}
|
||||
final int sz = cs.length();
|
||||
if (start < 0) {
|
||||
return NOT_FOUND;
|
||||
}
|
||||
if (start >= sz) {
|
||||
start = sz - 1;
|
||||
}
|
||||
if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||
for (int i = start; i >= 0; --i) {
|
||||
if (cs.charAt(i) == searchChar) {
|
||||
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--) {
|
||||
final char high = cs.charAt(i);
|
||||
final char low = cs.charAt(i + 1);
|
||||
if (chars[0] == high && chars[1] == low) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
static final int TO_STRING_LIMIT = 16;
|
||||
|
||||
/**
|
||||
* 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 find
|
||||
* @param start the start index
|
||||
* @return the index where the search sequence was found
|
||||
*/
|
||||
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);
|
||||
} else if (cs instanceof StringBuilder) {
|
||||
return ((StringBuilder) cs).lastIndexOf((String) searchChar, start);
|
||||
} else 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 < 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);
|
||||
} else if (cs instanceof StringBuilder) {
|
||||
return ((StringBuilder) cs).lastIndexOf(searchChar.toString(), start);
|
||||
} else 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given CharSequence to a char[].
|
||||
*
|
||||
* @param source the {@code CharSequence} to be processed.
|
||||
* @return the resulting char array, never null.
|
||||
* @since 3.11
|
||||
*/
|
||||
public static char[] toCharArray(final CharSequence source) {
|
||||
final int len = StringUtils.length(source);
|
||||
if (len == 0) {
|
||||
return ArrayUtils.EMPTY_CHAR_ARRAY;
|
||||
}
|
||||
if (source instanceof String) {
|
||||
return ((String) source).toCharArray();
|
||||
}
|
||||
final char[] array = new char[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
array[i] = source.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) {
|
||||
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++);
|
||||
|
||||
if (c1 == c2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ignoreCase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The real same check as in String.regionMatches():
|
||||
final char u1 = Character.toUpperCase(c1);
|
||||
final char u2 = Character.toUpperCase(c2);
|
||||
if (u1 != u2 && Character.toLowerCase(u1) != Character.toLowerCase(u2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
295
before/src/main/java/org/apache/commons/lang3/CharSet.java
Normal file
295
before/src/main/java/org/apache/commons/lang3/CharSet.java
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>A set of characters.</p>
|
||||
*
|
||||
* <p>Instances are immutable, but instances of subclasses may not be.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class CharSet implements Serializable {
|
||||
|
||||
/**
|
||||
* Required for serialization support. Lang version 2.0.
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 5947847346149275958L;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @since 2.0
|
||||
*/
|
||||
protected static final Map<String, CharSet> COMMON = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
static {
|
||||
COMMON.put(null, EMPTY);
|
||||
COMMON.put(StringUtils.EMPTY, EMPTY);
|
||||
COMMON.put("a-zA-Z", ASCII_ALPHA);
|
||||
COMMON.put("A-Za-z", ASCII_ALPHA);
|
||||
COMMON.put("a-z", ASCII_ALPHA_LOWER);
|
||||
COMMON.put("A-Z", ASCII_ALPHA_UPPER);
|
||||
COMMON.put("0-9", ASCII_NUMERIC);
|
||||
}
|
||||
|
||||
/** The set of CharRange objects. */
|
||||
private final Set<CharRange> set = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Factory method to create a new CharSet using a special syntax.</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code null} or empty string ("")
|
||||
* - set containing no characters</li>
|
||||
* <li>Single character, such as "a"
|
||||
* - set containing just that character</li>
|
||||
* <li>Multi character, such as "a-e"
|
||||
* - set containing characters from one character to the other</li>
|
||||
* <li>Negated, such as "^a" or "^a-e"
|
||||
* - set containing all characters except those defined</li>
|
||||
* <li>Combinations, such as "abe-g"
|
||||
* - set containing all the characters from the individual sets</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>The matching order is:</p>
|
||||
* <ol>
|
||||
* <li>Negated multi character range, such as "^a-e"
|
||||
* <li>Ordinary multi character range, such as "a-e"
|
||||
* <li>Negated single character, such as "^a"
|
||||
* <li>Ordinary single character, such as "a"
|
||||
* </ol>
|
||||
*
|
||||
* <p>Matching works left to right. Once a match is found the
|
||||
* search starts again from the next character.</p>
|
||||
*
|
||||
* <p>If the same range is defined twice using the same syntax, only
|
||||
* one range will be kept.
|
||||
* Thus, "a-ca-c" creates only one range of "a-c".</p>
|
||||
*
|
||||
* <p>If the start and end of a range are in the wrong order,
|
||||
* they are reversed. Thus "a-e" is the same as "e-a".
|
||||
* As a result, "a-ee-a" would create only one range,
|
||||
* as the "a-e" and "e-a" are the same.</p>
|
||||
*
|
||||
* <p>The set of characters represented is the union of the specified ranges.</p>
|
||||
*
|
||||
* <p>There are two ways to add a literal negation character ({@code ^}):</p>
|
||||
* <ul>
|
||||
* <li>As the last character in a string, e.g. {@code CharSet.getInstance("a-z^")}</li>
|
||||
* <li>As a separate element, e.g. {@code CharSet.getInstance("^", "a-z")}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Examples using the negation character:</p>
|
||||
* <pre>
|
||||
* 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
|
||||
* </pre>
|
||||
*
|
||||
* <p>All CharSet objects returned by this method will be immutable.</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
if (setStrs.length == 1) {
|
||||
final CharSet common = COMMON.get(setStrs[0]);
|
||||
if (common != null) {
|
||||
return common;
|
||||
}
|
||||
}
|
||||
return new CharSet(setStrs);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Constructs a new CharSet using the set syntax.
|
||||
* Each string is merged in with the set.</p>
|
||||
*
|
||||
* @param set Strings to merge into the initial set
|
||||
* @throws NullPointerException if set is {@code null}
|
||||
*/
|
||||
protected CharSet(final String... set) {
|
||||
for (final String s : set) {
|
||||
add(s);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Add a set definition string to the {@code CharSet}.</p>
|
||||
*
|
||||
* @param str set definition string
|
||||
*/
|
||||
protected void add(final String str) {
|
||||
if (str == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int len = str.length();
|
||||
int pos = 0;
|
||||
while (pos < len) {
|
||||
final int remainder = len - pos;
|
||||
if (remainder >= 4 && str.charAt(pos) == '^' && str.charAt(pos + 2) == '-') {
|
||||
// negated range
|
||||
set.add(CharRange.isNotIn(str.charAt(pos + 1), str.charAt(pos + 3)));
|
||||
pos += 4;
|
||||
} else if (remainder >= 3 && str.charAt(pos + 1) == '-') {
|
||||
// range
|
||||
set.add(CharRange.isIn(str.charAt(pos), str.charAt(pos + 2)));
|
||||
pos += 3;
|
||||
} else if (remainder >= 2 && str.charAt(pos) == '^') {
|
||||
// negated char
|
||||
set.add(CharRange.isNot(str.charAt(pos + 1)));
|
||||
pos += 2;
|
||||
} else {
|
||||
// char
|
||||
set.add(CharRange.is(str.charAt(pos)));
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Gets the internal set as an array of CharRange objects.</p>
|
||||
*
|
||||
* @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(CharRange.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Does the {@code CharSet} contain the specified
|
||||
* character {@code ch}.</p>
|
||||
*
|
||||
* @param ch the character to check for
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Basics
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Compares two {@code CharSet} objects, returning true if they represent
|
||||
* exactly the same set of characters defined in the same way.</p>
|
||||
*
|
||||
* <p>The two sets {@code abc} and {@code a-c} are <i>not</i>
|
||||
* equal according to this method.</p>
|
||||
*
|
||||
* @param obj the object to compare to
|
||||
* @return true if equal
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CharSet)) {
|
||||
return false;
|
||||
}
|
||||
final CharSet other = (CharSet) obj;
|
||||
return set.equals(other.set);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a hash code compatible with the equals method.</p>
|
||||
*
|
||||
* @return a suitable hash code
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 89 + set.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a string representation of the set.</p>
|
||||
*
|
||||
* @return string representation of the set
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return set.toString();
|
||||
}
|
||||
|
||||
}
|
248
before/src/main/java/org/apache/commons/lang3/CharSetUtils.java
Normal file
248
before/src/main/java/org/apache/commons/lang3/CharSetUtils.java
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>Operations on {@code CharSet} instances.</p>
|
||||
*
|
||||
* <p>This class handles {@code null} input gracefully.
|
||||
* An exception will not be thrown for a {@code null} input.
|
||||
* Each method documents its behavior in more detail.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @see CharSet
|
||||
* @since 1.0
|
||||
*/
|
||||
public class CharSetUtils {
|
||||
|
||||
/**
|
||||
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||
* and identifies whether any of the characters are present in the specified string.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharSetUtils.containsAny(null, *) = false
|
||||
* CharSetUtils.containsAny("", *) = false
|
||||
* CharSetUtils.containsAny(*, null) = false
|
||||
* CharSetUtils.containsAny(*, "") = false
|
||||
* CharSetUtils.containsAny("hello", "k-p") = true
|
||||
* CharSetUtils.containsAny("hello", "a-d") = false
|
||||
* </pre>
|
||||
*
|
||||
* @see CharSet#getInstance(java.lang.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)) {
|
||||
return false;
|
||||
}
|
||||
final CharSet chars = CharSet.getInstance(set);
|
||||
for (final char c : str.toCharArray()) {
|
||||
if (chars.contains(c)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||
* and returns the number of characters present in the specified string.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharSetUtils.count(null, *) = 0
|
||||
* CharSetUtils.count("", *) = 0
|
||||
* CharSetUtils.count(*, null) = 0
|
||||
* CharSetUtils.count(*, "") = 0
|
||||
* CharSetUtils.count("hello", "k-p") = 3
|
||||
* CharSetUtils.count("hello", "a-e") = 1
|
||||
* </pre>
|
||||
*
|
||||
* @see CharSet#getInstance(java.lang.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
|
||||
*/
|
||||
public static int count(final String str, final String... set) {
|
||||
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||
return 0;
|
||||
}
|
||||
final CharSet chars = CharSet.getInstance(set);
|
||||
int count = 0;
|
||||
for (final char c : str.toCharArray()) {
|
||||
if (chars.contains(c)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not all the Strings in an array are
|
||||
* empty or not.
|
||||
*
|
||||
* @param strings String[] whose elements are being checked for emptiness
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||
* and deletes any of characters present in the specified string.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharSetUtils.delete(null, *) = null
|
||||
* CharSetUtils.delete("", *) = ""
|
||||
* CharSetUtils.delete(*, null) = *
|
||||
* CharSetUtils.delete(*, "") = *
|
||||
* CharSetUtils.delete("hello", "hl") = "eo"
|
||||
* CharSetUtils.delete("hello", "le") = "ho"
|
||||
* </pre>
|
||||
*
|
||||
* @see CharSet#getInstance(java.lang.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
|
||||
*/
|
||||
public static String delete(final String str, final String... set) {
|
||||
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||
return str;
|
||||
}
|
||||
return modify(str, set, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||
* and keeps any of characters present in the specified string.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharSetUtils.keep(null, *) = null
|
||||
* CharSetUtils.keep("", *) = ""
|
||||
* CharSetUtils.keep(*, null) = ""
|
||||
* CharSetUtils.keep(*, "") = ""
|
||||
* CharSetUtils.keep("hello", "hl") = "hll"
|
||||
* CharSetUtils.keep("hello", "le") = "ell"
|
||||
* </pre>
|
||||
*
|
||||
* @see CharSet#getInstance(java.lang.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
|
||||
* @since 2.0
|
||||
*/
|
||||
public static String keep(final String str, final String... set) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
if (str.isEmpty() || deepEmpty(set)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return modify(str, set, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of 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
|
||||
*/
|
||||
private static String modify(final String str, final String[] set, final boolean expect) {
|
||||
final CharSet chars = CharSet.getInstance(set);
|
||||
final StringBuilder buffer = new StringBuilder(str.length());
|
||||
final char[] chrs = str.toCharArray();
|
||||
for (final char chr : chrs) {
|
||||
if (chars.contains(chr) == expect) {
|
||||
buffer.append(chr);
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Squeezes any repetitions of a character that is mentioned in the
|
||||
* supplied set.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharSetUtils.squeeze(null, *) = null
|
||||
* CharSetUtils.squeeze("", *) = ""
|
||||
* CharSetUtils.squeeze(*, null) = *
|
||||
* CharSetUtils.squeeze(*, "") = *
|
||||
* CharSetUtils.squeeze("hello", "k-p") = "helo"
|
||||
* CharSetUtils.squeeze("hello", "a-e") = "hello"
|
||||
* </pre>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static String squeeze(final String str, final String... set) {
|
||||
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||
return str;
|
||||
}
|
||||
final CharSet chars = CharSet.getInstance(set);
|
||||
final StringBuilder buffer = new StringBuilder(str.length());
|
||||
final char[] chrs = str.toCharArray();
|
||||
final int sz = chrs.length;
|
||||
char lastChar = chrs[0];
|
||||
char ch = ' ';
|
||||
Character inChars = null;
|
||||
Character notInChars = null;
|
||||
buffer.append(lastChar);
|
||||
for (int i = 1; i < sz; i++) {
|
||||
ch = chrs[i];
|
||||
if (ch == lastChar) {
|
||||
if (inChars != null && ch == inChars) {
|
||||
continue;
|
||||
}
|
||||
if (notInChars == null || ch != notInChars) {
|
||||
if (chars.contains(ch)) {
|
||||
inChars = ch;
|
||||
continue;
|
||||
}
|
||||
notInChars = ch;
|
||||
}
|
||||
}
|
||||
buffer.append(ch);
|
||||
lastChar = ch;
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>CharSetUtils instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||
* to operate.</p>
|
||||
*/
|
||||
public CharSetUtils() {
|
||||
}
|
||||
}
|
551
before/src/main/java/org/apache/commons/lang3/CharUtils.java
Normal file
551
before/src/main/java/org/apache/commons/lang3/CharUtils.java
Normal file
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>Operations on char primitives and Character objects.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 2.1
|
||||
*/
|
||||
public class CharUtils {
|
||||
|
||||
private static final String[] CHAR_STRING_ARRAY = new String[128];
|
||||
|
||||
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
/**
|
||||
* Linefeed character LF ({@code '\n'}, Unicode 000a).
|
||||
*
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
|
||||
* for Character and String Literals</a>
|
||||
* @since 2.2
|
||||
*/
|
||||
public static final char LF = '\n';
|
||||
|
||||
/**
|
||||
* Carriage return characterf CR ('\r', Unicode 000d).
|
||||
*
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
|
||||
* for Character and String Literals</a>
|
||||
* @since 2.2
|
||||
*/
|
||||
public static final char CR = '\r';
|
||||
|
||||
/**
|
||||
* {@code \u0000} null control character ('\0'), abbreviated NUL.
|
||||
*
|
||||
* @since 3.6
|
||||
*/
|
||||
public static final char NUL = '\0';
|
||||
|
||||
static {
|
||||
for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) {
|
||||
CHAR_STRING_ARRAY[c] = String.valueOf(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>{@code CharUtils} instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as {@code CharUtils.toString('c');}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||
* to operate.</p>
|
||||
*/
|
||||
public CharUtils() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the character to a Character.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same Character object each time.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toCharacterObject(' ') = ' '
|
||||
* CharUtils.toCharacterObject('A') = 'A'
|
||||
* </pre>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@Deprecated
|
||||
public static Character toCharacterObject(final char ch) {
|
||||
return Character.valueOf(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the String to a Character using the first character, returning
|
||||
* null for empty Strings.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same Character object each time.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toCharacterObject(null) = null
|
||||
* CharUtils.toCharacterObject("") = null
|
||||
* CharUtils.toCharacterObject("A") = 'A'
|
||||
* CharUtils.toCharacterObject("BA") = 'B'
|
||||
* </pre>
|
||||
*
|
||||
* @param str the character to convert
|
||||
* @return the Character value of the first letter of the String
|
||||
*/
|
||||
public static Character toCharacterObject(final String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
return Character.valueOf(str.charAt(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the Character to a char throwing an exception for {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar(' ') = ' '
|
||||
* CharUtils.toChar('A') = 'A'
|
||||
* CharUtils.toChar(null) throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @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.notNull(ch, "ch");
|
||||
return ch.charValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the Character to a char handling {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar(null, 'X') = 'X'
|
||||
* CharUtils.toChar(' ', 'X') = ' '
|
||||
* CharUtils.toChar('A', 'X') = 'A'
|
||||
* </pre>
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the String to a char using the first character, throwing
|
||||
* an exception on empty Strings.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar("A") = 'A'
|
||||
* CharUtils.toChar("BA") = 'B'
|
||||
* CharUtils.toChar(null) throws IllegalArgumentException
|
||||
* CharUtils.toChar("") throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @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.notEmpty(str, "The String must not be empty");
|
||||
return str.charAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the String to a char using the first character, defaulting
|
||||
* the value on empty Strings.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toChar(null, 'X') = 'X'
|
||||
* CharUtils.toChar("", 'X') = 'X'
|
||||
* CharUtils.toChar("A", 'X') = 'A'
|
||||
* CharUtils.toChar("BA", 'X') = 'B'
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue('3') = 3
|
||||
* CharUtils.toIntValue('A') throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @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) {
|
||||
if (!isAsciiNumeric(ch)) {
|
||||
throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'");
|
||||
}
|
||||
return ch - 48;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue('3', -1) = 3
|
||||
* CharUtils.toIntValue('A', -1) = -1
|
||||
* </pre>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue('3') = 3
|
||||
* CharUtils.toIntValue(null) throws IllegalArgumentException
|
||||
* CharUtils.toIntValue('A') throws IllegalArgumentException
|
||||
* </pre>
|
||||
*
|
||||
* @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.notNull(ch, "ch");
|
||||
return toIntValue(ch.charValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to the Integer it represents, throwing an
|
||||
* exception if the character is not numeric.</p>
|
||||
*
|
||||
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toIntValue(null, -1) = -1
|
||||
* CharUtils.toIntValue('3', -1) = 3
|
||||
* CharUtils.toIntValue('A', -1) = -1
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the character to a String that contains the one character.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same String object each time.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toString(' ') = " "
|
||||
* CharUtils.toString('A') = "A"
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to convert
|
||||
* @return a String containing the one specified character
|
||||
*/
|
||||
public static String toString(final char ch) {
|
||||
if (ch < 128) {
|
||||
return CHAR_STRING_ARRAY[ch];
|
||||
}
|
||||
return new String(new char[] {ch});
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the character to a String that contains the one character.</p>
|
||||
*
|
||||
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||
* same String object each time.</p>
|
||||
*
|
||||
* <p>If {@code null} is passed in, {@code null} will be returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.toString(null) = null
|
||||
* CharUtils.toString(' ') = " "
|
||||
* CharUtils.toString('A') = "A"
|
||||
* </pre>
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Converts the string to the Unicode format '\u0020'.</p>
|
||||
*
|
||||
* <p>This format is the Java source code format.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.unicodeEscaped(' ') = "\u0020"
|
||||
* CharUtils.unicodeEscaped('A') = "\u0041"
|
||||
* </pre>
|
||||
*
|
||||
* @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];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the string to the Unicode format '\u0020'.</p>
|
||||
*
|
||||
* <p>This format is the Java source code format.</p>
|
||||
*
|
||||
* <p>If {@code null} is passed in, {@code null} will be returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.unicodeEscaped(null) = null
|
||||
* CharUtils.unicodeEscaped(' ') = "\u0020"
|
||||
* CharUtils.unicodeEscaped('A') = "\u0041"
|
||||
* </pre>
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAscii('a') = true
|
||||
* CharUtils.isAscii('A') = true
|
||||
* CharUtils.isAscii('3') = true
|
||||
* CharUtils.isAscii('-') = true
|
||||
* CharUtils.isAscii('\n') = true
|
||||
* CharUtils.isAscii('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @param ch the character to check
|
||||
* @return true if less than 128
|
||||
*/
|
||||
public static boolean isAscii(final char ch) {
|
||||
return ch < 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit printable.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiPrintable('a') = true
|
||||
* CharUtils.isAsciiPrintable('A') = true
|
||||
* CharUtils.isAsciiPrintable('3') = true
|
||||
* CharUtils.isAsciiPrintable('-') = true
|
||||
* CharUtils.isAsciiPrintable('\n') = false
|
||||
* CharUtils.isAsciiPrintable('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit control.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiControl('a') = false
|
||||
* CharUtils.isAsciiControl('A') = false
|
||||
* CharUtils.isAsciiControl('3') = false
|
||||
* CharUtils.isAsciiControl('-') = false
|
||||
* CharUtils.isAsciiControl('\n') = true
|
||||
* CharUtils.isAsciiControl('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit alphabetic.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlpha('a') = true
|
||||
* CharUtils.isAsciiAlpha('A') = true
|
||||
* CharUtils.isAsciiAlpha('3') = false
|
||||
* CharUtils.isAsciiAlpha('-') = false
|
||||
* CharUtils.isAsciiAlpha('\n') = false
|
||||
* CharUtils.isAsciiAlpha('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit alphabetic upper case.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlphaUpper('a') = false
|
||||
* CharUtils.isAsciiAlphaUpper('A') = true
|
||||
* CharUtils.isAsciiAlphaUpper('3') = false
|
||||
* CharUtils.isAsciiAlphaUpper('-') = false
|
||||
* CharUtils.isAsciiAlphaUpper('\n') = false
|
||||
* CharUtils.isAsciiAlphaUpper('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit alphabetic lower case.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlphaLower('a') = true
|
||||
* CharUtils.isAsciiAlphaLower('A') = false
|
||||
* CharUtils.isAsciiAlphaLower('3') = false
|
||||
* CharUtils.isAsciiAlphaLower('-') = false
|
||||
* CharUtils.isAsciiAlphaLower('\n') = false
|
||||
* CharUtils.isAsciiAlphaLower('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit numeric.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiNumeric('a') = false
|
||||
* CharUtils.isAsciiNumeric('A') = false
|
||||
* CharUtils.isAsciiNumeric('3') = true
|
||||
* CharUtils.isAsciiNumeric('-') = false
|
||||
* CharUtils.isAsciiNumeric('\n') = false
|
||||
* CharUtils.isAsciiNumeric('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the character is ASCII 7 bit numeric.</p>
|
||||
*
|
||||
* <pre>
|
||||
* CharUtils.isAsciiAlphanumeric('a') = true
|
||||
* CharUtils.isAsciiAlphanumeric('A') = true
|
||||
* CharUtils.isAsciiAlphanumeric('3') = true
|
||||
* CharUtils.isAsciiAlphanumeric('-') = false
|
||||
* CharUtils.isAsciiAlphanumeric('\n') = false
|
||||
* CharUtils.isAsciiAlphanumeric('©') = false
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
69
before/src/main/java/org/apache/commons/lang3/Charsets.java
Normal file
69
before/src/main/java/org/apache/commons/lang3/Charsets.java
Normal file
|
@ -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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.nio.charset.Charset;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
* <p>
|
||||
* Provides utilities for {@link Charset}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Package private since Apache Commons IO already provides a Charsets because {@link Charset} is in
|
||||
* {@code java.nio.charset}.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static Charset toCharset(final Charset charset) {
|
||||
return charset == null ? Charset.defaultCharset() : 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.
|
||||
* @throws UnsupportedCharsetException If no support for the named charset is available in this instance of the Java
|
||||
* virtual machine
|
||||
*/
|
||||
static Charset toCharset(final String charsetName) {
|
||||
return charsetName == null ? Charset.defaultCharset() : Charset.forName(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.
|
||||
*/
|
||||
static String toCharsetName(final String charsetName) {
|
||||
return charsetName == null ? Charset.defaultCharset().name() : charsetName;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Helps work with {@link ClassLoader}.
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
public class ClassLoaderUtils {
|
||||
|
||||
/**
|
||||
* Converts the given class loader to a String calling {@link #toString(URLClassLoader)}.
|
||||
*
|
||||
* @param classLoader to URLClassLoader to convert.
|
||||
* @return the formatted string.
|
||||
*/
|
||||
public static String toString(final ClassLoader classLoader) {
|
||||
if (classLoader instanceof URLClassLoader) {
|
||||
return toString((URLClassLoader) classLoader);
|
||||
}
|
||||
return classLoader.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given URLClassLoader to a String in the format
|
||||
* {@code "URLClassLoader.toString() + [URL1, URL2, ...]"}.
|
||||
*
|
||||
* @param classLoader to URLClassLoader to convert.
|
||||
* @return the formatted string.
|
||||
*/
|
||||
public static String toString(final URLClassLoader classLoader) {
|
||||
return classLoader + Arrays.toString(classLoader.getURLs());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* Operations regarding the classpath.
|
||||
*
|
||||
* <p>The methods of this class do not allow {@code null} inputs.</p>
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
//@Immutable
|
||||
public class ClassPathUtils {
|
||||
|
||||
/**
|
||||
* <p>{@code ClassPathUtils} instances should NOT be constructed in
|
||||
* standard programming. Instead, the class should be used as
|
||||
* {@code ClassPathUtils.toFullyQualifiedName(MyClass.class, "MyClass.properties");}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public ClassPathUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully qualified name for the resource with name {@code resourceName} relative to the given context.
|
||||
*
|
||||
* <p>Note that this method does not check whether the resource actually exists.
|
||||
* It only constructs the name.
|
||||
* Null inputs are not allowed.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ClassPathUtils.toFullyQualifiedName(StringUtils.class, "StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
|
||||
* </pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static String toFullyQualifiedName(final Class<?> context, final String resourceName) {
|
||||
Validate.notNull(context, "context" );
|
||||
Validate.notNull(resourceName, "resourceName");
|
||||
return toFullyQualifiedName(context.getPackage(), resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully qualified name for the resource with name {@code resourceName} relative to the given context.
|
||||
*
|
||||
* <p>Note that this method does not check whether the resource actually exists.
|
||||
* It only constructs the name.
|
||||
* Null inputs are not allowed.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ClassPathUtils.toFullyQualifiedName(StringUtils.class.getPackage(), "StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
|
||||
* </pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static String toFullyQualifiedName(final Package context, final String resourceName) {
|
||||
Validate.notNull(context, "context" );
|
||||
Validate.notNull(resourceName, "resourceName");
|
||||
return context.getName() + "." + resourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully qualified path for the resource with name {@code resourceName} relative to the given context.
|
||||
*
|
||||
* <p>Note that this method does not check whether the resource actually exists.
|
||||
* It only constructs the path.
|
||||
* Null inputs are not allowed.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ClassPathUtils.toFullyQualifiedPath(StringUtils.class, "StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
|
||||
* </pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static String toFullyQualifiedPath(final Class<?> context, final String resourceName) {
|
||||
Validate.notNull(context, "context" );
|
||||
Validate.notNull(resourceName, "resourceName");
|
||||
return toFullyQualifiedPath(context.getPackage(), resourceName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fully qualified path for the resource with name {@code resourceName} relative to the given context.
|
||||
*
|
||||
* <p>Note that this method does not check whether the resource actually exists.
|
||||
* It only constructs the path.
|
||||
* Null inputs are not allowed.</p>
|
||||
*
|
||||
* <pre>
|
||||
* ClassPathUtils.toFullyQualifiedPath(StringUtils.class.getPackage(), "StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
|
||||
* </pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static String toFullyQualifiedPath(final Package context, final String resourceName) {
|
||||
Validate.notNull(context, "context" );
|
||||
Validate.notNull(resourceName, "resourceName");
|
||||
return context.getName().replace('.', '/') + "/" + resourceName;
|
||||
}
|
||||
|
||||
}
|
1562
before/src/main/java/org/apache/commons/lang3/ClassUtils.java
Normal file
1562
before/src/main/java/org/apache/commons/lang3/ClassUtils.java
Normal file
File diff suppressed because it is too large
Load diff
1563
before/src/main/java/org/apache/commons/lang3/Conversion.java
Normal file
1563
before/src/main/java/org/apache/commons/lang3/Conversion.java
Normal file
File diff suppressed because it is too large
Load diff
379
before/src/main/java/org/apache/commons/lang3/EnumUtils.java
Normal file
379
before/src/main/java/org/apache/commons/lang3/EnumUtils.java
Normal file
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Utility library to provide helper methods for Java enums.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
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.";
|
||||
|
||||
/**
|
||||
* Validate {@code enumClass}.
|
||||
* @param <E> 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 <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
|
||||
Validate.notNull(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 <E> 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 <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> 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));
|
||||
|
||||
return enumClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a long bit vector representation of the given array of Enum values.</p>
|
||||
*
|
||||
* <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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 <E> 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)
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
|
||||
Validate.noNullElements(values);
|
||||
return generateBitVector(enumClass, Arrays.asList(values));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a long bit vector representation of the given subset of an Enum.</p>
|
||||
*
|
||||
* <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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 <E> 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}
|
||||
* @since 3.0.1
|
||||
* @see #generateBitVectors(Class, Iterable)
|
||||
*/
|
||||
public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<? extends E> values) {
|
||||
checkBitVectorable(enumClass);
|
||||
Validate.notNull(values);
|
||||
long total = 0;
|
||||
for (final E constant : values) {
|
||||
Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
|
||||
total |= 1L << constant.ordinal();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
|
||||
*
|
||||
* <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
|
||||
*
|
||||
* <p>Use this method if you have more than 64 values in your Enum.</p>
|
||||
*
|
||||
* @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 <E> 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}
|
||||
* @since 3.2
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) {
|
||||
asEnum(enumClass);
|
||||
Validate.noNullElements(values);
|
||||
final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
|
||||
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);
|
||||
}
|
||||
ArrayUtils.reverse(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
|
||||
*
|
||||
* <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
|
||||
*
|
||||
* <p>Use this method if you have more than 64 values in your Enum.</p>
|
||||
*
|
||||
* @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 <E> 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}
|
||||
* @since 3.2
|
||||
*/
|
||||
public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<? extends E> values) {
|
||||
asEnum(enumClass);
|
||||
Validate.notNull(values);
|
||||
final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
|
||||
for (final E constant : values) {
|
||||
Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
|
||||
condensed.add(constant);
|
||||
}
|
||||
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);
|
||||
}
|
||||
ArrayUtils.reverse(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the enum for the class, returning {@code null} if not found.</p>
|
||||
*
|
||||
* <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
|
||||
* for an invalid enum name.</p>
|
||||
*
|
||||
* @param <E> 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 extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName) {
|
||||
return getEnum(enumClass, enumName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the enum for the class, returning {@code defaultEnum} if not found.</p>
|
||||
*
|
||||
* <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
|
||||
* for an invalid enum name.</p>
|
||||
*
|
||||
* @param <E> 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
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName, final E defaultEnum) {
|
||||
if (enumName == null) {
|
||||
return defaultEnum;
|
||||
}
|
||||
try {
|
||||
return Enum.valueOf(enumClass, enumName);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
return defaultEnum;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the enum for the class, returning {@code null} if not found.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param <E> 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
|
||||
* @since 3.8
|
||||
*/
|
||||
public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
|
||||
return getEnumIgnoreCase(enumClass, enumName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the enum for the class, returning {@code defaultEnum} if not found.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param <E> 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
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the {@code List} of enums.</p>
|
||||
*
|
||||
* <p>This method is useful when you need a list of enums rather than an array.</p>
|
||||
*
|
||||
* @param <E> 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 <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) {
|
||||
return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants()));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the {@code Map} of enums by name.</p>
|
||||
*
|
||||
* <p>This method is useful when you need a map of enums by name.</p>
|
||||
*
|
||||
* @param <E> 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 <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) {
|
||||
final Map<String, E> map = new LinkedHashMap<>();
|
||||
for (final E e: enumClass.getEnumConstants()) {
|
||||
map.put(e.name(), e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if the specified name is a valid enum for the class.</p>
|
||||
*
|
||||
* <p>This method differs from {@link Enum#valueOf} in that checks if the name is
|
||||
* a valid enum without needing to catch the exception.</p>
|
||||
*
|
||||
* @param <E> 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
|
||||
*/
|
||||
public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) {
|
||||
return getEnum(enumClass, enumName) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if the specified name is a valid enum for the class.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param <E> 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
|
||||
* @since 3.8
|
||||
*/
|
||||
public static <E extends Enum<E>> boolean isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
|
||||
return getEnumIgnoreCase(enumClass, enumName) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a long value created by {@link EnumUtils#generateBitVector} into the set of
|
||||
* enum values that it represents.</p>
|
||||
*
|
||||
* <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
|
||||
* @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 <E> 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 <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) {
|
||||
checkBitVectorable(enumClass).getEnumConstants();
|
||||
return processBitVectors(enumClass, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of
|
||||
* enum values that it represents.</p>
|
||||
*
|
||||
* <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
|
||||
* @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 <E> 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 <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, final long... values) {
|
||||
final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass));
|
||||
final long[] lvalues = ArrayUtils.clone(Validate.notNull(values));
|
||||
ArrayUtils.reverse(lvalues);
|
||||
for (final E constant : enumClass.getEnumConstants()) {
|
||||
final int block = constant.ordinal() / Long.SIZE;
|
||||
if (block < lvalues.length && (lvalues[block] & 1L << (constant.ordinal() % Long.SIZE)) != 0) {
|
||||
results.add(constant);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.
|
||||
*/
|
||||
public EnumUtils() {
|
||||
}
|
||||
}
|
693
before/src/main/java/org/apache/commons/lang3/Functions.java
Normal file
693
before/src/main/java/org/apache/commons/lang3/Functions.java
Normal file
|
@ -0,0 +1,693 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.Streams.FailableStream;
|
||||
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:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* Consumer<java.lang.reflect.Method> consumer = m -> {
|
||||
* try {
|
||||
* m.invoke(o, args);
|
||||
* } catch (Throwable t) {
|
||||
* throw Functions.rethrow(t);
|
||||
* }
|
||||
* };
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* By replacing a {@link java.util.function.Consumer Consumer<O>} with a {@link FailableConsumer
|
||||
* FailableConsumer<O,? extends Throwable>}, this can be written like follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* Functions.accept((m) -> m.invoke(o,args));
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Obviously, the second version is much more concise and the spirit of Lambda expressions is met better than the second
|
||||
* version.
|
||||
* </p>
|
||||
* @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 {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <O1> Consumed type 1.
|
||||
* @param <O2> Consumed type 2.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableBiConsumer}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableBiConsumer<O1, O2, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* 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 accept(O1 object1, O2 object2) throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link BiFunction} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <O1> Input type 1.
|
||||
* @param <O2> Input type 2.
|
||||
* @param <R> Return type.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableBiFunction}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableBiFunction<O1, O2, R, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
R apply(O1 input1, O2 input2) throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link BiPredicate} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <O1> Predicate type 1.
|
||||
* @param <O2> Predicate type 2.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableBiPredicate}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableBiPredicate<O1, O2, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
boolean test(O1 object1, O2 object2) throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link java.util.concurrent.Callable} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <R> Return type.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableCallable}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableCallable<R, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Calls the callable.
|
||||
*
|
||||
* @return The value returned from the callable
|
||||
* @throws T if the callable fails
|
||||
*/
|
||||
R call() throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link Consumer} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <O> Consumed type 1.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableConsumer}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableConsumer<O, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Accepts the consumer.
|
||||
*
|
||||
* @param object the parameter for the consumable to accept
|
||||
* @throws T Thrown when the consumer fails.
|
||||
*/
|
||||
void accept(O object) throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link Function} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <I> Input type 1.
|
||||
* @param <R> Return type.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableFunction}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableFunction<I, R, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Applies this function.
|
||||
*
|
||||
* @param input the input for the function
|
||||
* @return the result of the function
|
||||
* @throws T Thrown when the function fails.
|
||||
*/
|
||||
R apply(I input) throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link Predicate} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <I> Predicate type 1.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailablePredicate}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailablePredicate<I, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* 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(I object) throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link Runnable} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableRunnable}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableRunnable<T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Runs the function.
|
||||
*
|
||||
* @throws T Thrown when the function fails.
|
||||
*/
|
||||
void run() throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface like {@link Supplier} that declares a {@code Throwable}.
|
||||
*
|
||||
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||
*
|
||||
* @param <R> Return type.
|
||||
* @param <T> Thrown exception.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.function.FailableSupplier}.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface FailableSupplier<R, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Supplies an object
|
||||
*
|
||||
* @return a result
|
||||
* @throws T if the supplier fails
|
||||
*/
|
||||
R get() throws T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes a consumer and rethrows any exception as a {@link RuntimeException}.
|
||||
*
|
||||
* @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 <O1> the type of the first argument the consumer accepts
|
||||
* @param <O2> the type of the second argument the consumer accepts
|
||||
* @param <T> the type of checked exception the consumer may throw
|
||||
*/
|
||||
public static <O1, O2, T extends Throwable> void accept(final FailableBiConsumer<O1, O2, T> consumer,
|
||||
final O1 object1, final O2 object2) {
|
||||
run(() -> consumer.accept(object1, object2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes a consumer and rethrows any exception as a {@link RuntimeException}.
|
||||
*
|
||||
* @param consumer the consumer to consume
|
||||
* @param object the object to consume by {@code consumer}
|
||||
* @param <O> the type the consumer accepts
|
||||
* @param <T> the type of checked exception the consumer may throw
|
||||
*/
|
||||
public static <O, T extends Throwable> void accept(final FailableConsumer<O, T> consumer, final O object) {
|
||||
run(() -> consumer.accept(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a function and rethrows any exception as a {@link RuntimeException}.
|
||||
*
|
||||
* @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 <O1> the type of the first argument the function accepts
|
||||
* @param <O2> the type of the second argument the function accepts
|
||||
* @param <O> the return type of the function
|
||||
* @param <T> the type of checked exception the function may throw
|
||||
* @return the value returned from the function
|
||||
*/
|
||||
public static <O1, O2, O, T extends Throwable> O apply(final FailableBiFunction<O1, O2, O, T> function,
|
||||
final O1 input1, final O2 input2) {
|
||||
return get(() -> function.apply(input1, input2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a function and rethrows any exception as a {@link RuntimeException}.
|
||||
*
|
||||
* @param function the function to apply
|
||||
* @param input the input to apply {@code function} on
|
||||
* @param <I> the type of the argument the function accepts
|
||||
* @param <O> the return type of the function
|
||||
* @param <T> the type of checked exception the function may throw
|
||||
* @return the value returned from the function
|
||||
*/
|
||||
public static <I, O, T extends Throwable> O apply(final FailableFunction<I, O, T> function, final I input) {
|
||||
return get(() -> function.apply(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableBiConsumer} into a standard {@link BiConsumer}.
|
||||
*
|
||||
* @param <O1> the type of the first argument of the consumers
|
||||
* @param <O2> the type of the second argument of the consumers
|
||||
* @param consumer a failable {@code BiConsumer}
|
||||
* @return a standard {@code BiConsumer}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O1, O2> BiConsumer<O1, O2> asBiConsumer(final FailableBiConsumer<O1, O2, ?> consumer) {
|
||||
return (input1, input2) -> accept(consumer, input1, input2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableBiFunction} into a standard {@link BiFunction}.
|
||||
*
|
||||
* @param <O1> the type of the first argument of the input of the functions
|
||||
* @param <O2> the type of the second argument of the input of the functions
|
||||
* @param <O> the type of the output of the functions
|
||||
* @param function a {@code FailableBiFunction}
|
||||
* @return a standard {@code BiFunction}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O1, O2, O> BiFunction<O1, O2, O> asBiFunction(final FailableBiFunction<O1, O2, O, ?> function) {
|
||||
return (input1, input2) -> apply(function, input1, input2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableBiPredicate} into a standard {@link BiPredicate}.
|
||||
*
|
||||
* @param <O1> the type of the first argument used by the predicates
|
||||
* @param <O2> the type of the second argument used by the predicates
|
||||
* @param predicate a {@code FailableBiPredicate}
|
||||
* @return a standard {@code BiPredicate}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O1, O2> BiPredicate<O1, O2> asBiPredicate(final FailableBiPredicate<O1, O2, ?> predicate) {
|
||||
return (input1, input2) -> test(predicate, input1, input2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableCallable} into a standard {@link Callable}.
|
||||
*
|
||||
* @param <O> the type used by the callables
|
||||
* @param callable a {@code FailableCallable}
|
||||
* @return a standard {@code Callable}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O> Callable<O> asCallable(final FailableCallable<O, ?> callable) {
|
||||
return () -> call(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableConsumer} into a standard {@link Consumer}.
|
||||
*
|
||||
* @param <I> the type used by the consumers
|
||||
* @param consumer a {@code FailableConsumer}
|
||||
* @return a standard {@code Consumer}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <I> Consumer<I> asConsumer(final FailableConsumer<I, ?> consumer) {
|
||||
return input -> accept(consumer, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableFunction} into a standard {@link Function}.
|
||||
*
|
||||
* @param <I> the type of the input of the functions
|
||||
* @param <O> the type of the output of the functions
|
||||
* @param function a {code FailableFunction}
|
||||
* @return a standard {@code Function}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <I, O> Function<I, O> asFunction(final FailableFunction<I, O, ?> function) {
|
||||
return input -> apply(function, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailablePredicate} into a standard {@link Predicate}.
|
||||
*
|
||||
* @param <I> the type used by the predicates
|
||||
* @param predicate a {@code FailablePredicate}
|
||||
* @return a standard {@code Predicate}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <I> Predicate<I> asPredicate(final FailablePredicate<I, ?> predicate) {
|
||||
return input -> test(predicate, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableRunnable} into a standard {@link Runnable}.
|
||||
*
|
||||
* @param runnable a {@code FailableRunnable}
|
||||
* @return a standard {@code Runnable}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static Runnable asRunnable(final FailableRunnable<?> runnable) {
|
||||
return () -> run(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link FailableSupplier} into a standard {@link Supplier}.
|
||||
*
|
||||
* @param <O> the type supplied by the suppliers
|
||||
* @param supplier a {@code FailableSupplier}
|
||||
* @return a standard {@code Supplier}
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O> Supplier<O> asSupplier(final FailableSupplier<O, ?> supplier) {
|
||||
return () -> get(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a callable and rethrows any exception as a {@link RuntimeException}.
|
||||
*
|
||||
* @param callable the callable to call
|
||||
* @param <O> the return type of the callable
|
||||
* @param <T> the type of checked exception the callable may throw
|
||||
* @return the value returned from the callable
|
||||
*/
|
||||
public static <O, T extends Throwable> O call(final FailableCallable<O, T> callable) {
|
||||
return get(callable::call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a supplier, and returns the result.
|
||||
*
|
||||
* @param supplier The supplier to invoke.
|
||||
* @param <O> The suppliers output type.
|
||||
* @param <T> 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, T extends Throwable> O get(final FailableSupplier<O, T> supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (final Throwable t) {
|
||||
throw rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a boolean supplier, and returns the result.
|
||||
*
|
||||
* @param supplier The boolean supplier to invoke.
|
||||
* @param <T> The type of checked exception, which the supplier can throw.
|
||||
* @return The boolean, which has been created by the supplier
|
||||
*/
|
||||
private static <T extends Throwable> boolean getAsBoolean(final FailableBooleanSupplier<T> supplier) {
|
||||
try {
|
||||
return supplier.getAsBoolean();
|
||||
} catch (final Throwable t) {
|
||||
throw rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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}
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* throw rethrow(myUncheckedException);</pre>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @param throwable The throwable to rethrow ossibly wrapped into an unchecked exception
|
||||
* @return Never returns anything, this method never terminates normally.
|
||||
*/
|
||||
public static RuntimeException rethrow(final Throwable throwable) {
|
||||
Objects.requireNonNull(throwable, "throwable");
|
||||
if (throwable instanceof RuntimeException) {
|
||||
throw (RuntimeException) throwable;
|
||||
} else if (throwable instanceof Error) {
|
||||
throw (Error) throwable;
|
||||
} else if (throwable instanceof IOException) {
|
||||
throw new UncheckedIOException((IOException) throwable);
|
||||
} else {
|
||||
throw new UndeclaredThrowableException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a runnable and rethrows any exception as a {@link RuntimeException}.
|
||||
*
|
||||
* @param runnable The runnable to run
|
||||
* @param <T> the type of checked exception the runnable may throw
|
||||
*/
|
||||
public static <T extends Throwable> void run(final FailableRunnable<T> runnable) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (final Throwable t) {
|
||||
throw rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given collection into a {@link FailableStream}. The {@link FailableStream} consists of the
|
||||
* collections elements. Shortcut for
|
||||
*
|
||||
* <pre>
|
||||
* Functions.stream(collection.stream());</pre>
|
||||
*
|
||||
* @param collection The collection, which is being converted into a {@link FailableStream}.
|
||||
* @param <O> The collections element type. (In turn, the result streams element type.)
|
||||
* @return The created {@link FailableStream}.
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O> FailableStream<O> stream(final Collection<O> collection) {
|
||||
return new FailableStream<>(collection.stream());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <O> The streams element type.
|
||||
* @return The created {@link FailableStream}.
|
||||
* @since 3.10
|
||||
*/
|
||||
public static <O> FailableStream<O> stream(final Stream<O> 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 <O1> the type of the first argument the predicate tests
|
||||
* @param <O2> the type of the second argument the predicate tests
|
||||
* @param <T> the type of checked exception the predicate may throw
|
||||
* @return the boolean value returned by the predicate
|
||||
*/
|
||||
public static <O1, O2, T extends Throwable> boolean test(final FailableBiPredicate<O1, O2, T> predicate,
|
||||
final O1 object1, final O2 object2) {
|
||||
return getAsBoolean(() -> predicate.test(object1, object2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <O> the type of argument the predicate tests
|
||||
* @param <T> the type of checked exception the predicate may throw
|
||||
* @return the boolean value returned by the predicate
|
||||
*/
|
||||
public static <O, T extends Throwable> boolean test(final FailablePredicate<O, T> 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 <em>all</em>
|
||||
* 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 <em>first</em> failure (AKA
|
||||
* {@link Throwable} is rethrown. Example use:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* final FileInputStream fis = new FileInputStream("my.file");
|
||||
* Functions.tryWithResources(useInputStream(fis), null, () -> fis.close());
|
||||
* }</pre>
|
||||
*
|
||||
* @param action The action to execute. This object <em>will</em> 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. <em>All</em> 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(final FailableRunnable<? extends Throwable> action,
|
||||
final FailableConsumer<Throwable, ? extends Throwable> errorHandler,
|
||||
final FailableRunnable<? extends Throwable>... resources) {
|
||||
final FailableConsumer<Throwable, ? extends Throwable> actualErrorHandler;
|
||||
if (errorHandler == null) {
|
||||
actualErrorHandler = Functions::rethrow;
|
||||
} else {
|
||||
actualErrorHandler = errorHandler;
|
||||
}
|
||||
if (resources != null) {
|
||||
for (final FailableRunnable<? extends Throwable> failableRunnable : resources) {
|
||||
Objects.requireNonNull(failableRunnable, "runnable");
|
||||
}
|
||||
}
|
||||
Throwable th = null;
|
||||
try {
|
||||
action.run();
|
||||
} catch (final Throwable t) {
|
||||
th = t;
|
||||
}
|
||||
if (resources != null) {
|
||||
for (final FailableRunnable<?> runnable : resources) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (final Throwable t) {
|
||||
if (th == null) {
|
||||
th = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (th != null) {
|
||||
try {
|
||||
actualErrorHandler.accept(th);
|
||||
} catch (final Throwable t) {
|
||||
throw rethrow(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <em>all</em>
|
||||
* 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 <em>first</em> failure (AKA
|
||||
* {@link Throwable} is rethrown. Example use:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* final FileInputStream fis = new FileInputStream("my.file");
|
||||
* Functions.tryWithResources(useInputStream(fis), () -> fis.close());
|
||||
* }</pre>
|
||||
*
|
||||
* @param action The action to execute. This object <em>will</em> always be invoked.
|
||||
* @param resources The resource actions to execute. <em>All</em> 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(final FailableRunnable<? extends Throwable> action,
|
||||
final FailableRunnable<? extends Throwable>... resources) {
|
||||
tryWithResources(action, null, resources);
|
||||
}
|
||||
}
|
324
before/src/main/java/org/apache/commons/lang3/JavaVersion.java
Normal file
324
before/src/main/java/org/apache/commons/lang3/JavaVersion.java
Normal file
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
/**
|
||||
* <p>An enum representing all the versions of the Java specification.
|
||||
* This is intended to mirror available values from the
|
||||
* <em>java.specification.version</em> System property. </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public enum JavaVersion {
|
||||
|
||||
/**
|
||||
* The Java version reported by Android. This is not an official Java version number.
|
||||
*/
|
||||
JAVA_0_9(1.5f, "0.9"),
|
||||
|
||||
/**
|
||||
* Java 1.1.
|
||||
*/
|
||||
JAVA_1_1(1.1f, "1.1"),
|
||||
|
||||
/**
|
||||
* Java 1.2.
|
||||
*/
|
||||
JAVA_1_2(1.2f, "1.2"),
|
||||
|
||||
/**
|
||||
* Java 1.3.
|
||||
*/
|
||||
JAVA_1_3(1.3f, "1.3"),
|
||||
|
||||
/**
|
||||
* Java 1.4.
|
||||
*/
|
||||
JAVA_1_4(1.4f, "1.4"),
|
||||
|
||||
/**
|
||||
* Java 1.5.
|
||||
*/
|
||||
JAVA_1_5(1.5f, "1.5"),
|
||||
|
||||
/**
|
||||
* Java 1.6.
|
||||
*/
|
||||
JAVA_1_6(1.6f, "1.6"),
|
||||
|
||||
/**
|
||||
* Java 1.7.
|
||||
*/
|
||||
JAVA_1_7(1.7f, "1.7"),
|
||||
|
||||
/**
|
||||
* Java 1.8.
|
||||
*/
|
||||
JAVA_1_8(1.8f, "1.8"),
|
||||
|
||||
/**
|
||||
* Java 1.9.
|
||||
*
|
||||
* @deprecated As of release 3.5, replaced by {@link #JAVA_9}
|
||||
*/
|
||||
@Deprecated
|
||||
JAVA_1_9(9.0f, "9"),
|
||||
|
||||
/**
|
||||
* Java 9.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
JAVA_9(9.0f, "9"),
|
||||
|
||||
/**
|
||||
* Java 10.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
JAVA_10(10.0f, "10"),
|
||||
|
||||
/**
|
||||
* Java 11.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
JAVA_11(11.0f, "11"),
|
||||
|
||||
/**
|
||||
* Java 12.
|
||||
*
|
||||
* @since 3.9
|
||||
*/
|
||||
JAVA_12(12.0f, "12"),
|
||||
|
||||
/**
|
||||
* Java 13.
|
||||
*
|
||||
* @since 3.9
|
||||
*/
|
||||
JAVA_13(13.0f, "13"),
|
||||
|
||||
/**
|
||||
* Java 14.
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
JAVA_14(14.0f, "14"),
|
||||
|
||||
/**
|
||||
* Java 15.
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
JAVA_15(15.0f, "15"),
|
||||
|
||||
/**
|
||||
* Java 16.
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
JAVA_16(16.0f, "16"),
|
||||
|
||||
/**
|
||||
* Java 17.
|
||||
*
|
||||
* @since 3.12.0
|
||||
*/
|
||||
JAVA_17(17.0f, "17"),
|
||||
|
||||
/**
|
||||
* 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()));
|
||||
|
||||
/**
|
||||
* The float value.
|
||||
*/
|
||||
private final float value;
|
||||
|
||||
/**
|
||||
* The standard name.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Whether this version of Java is at least the version of Java passed in.</p>
|
||||
*
|
||||
* <p>For example:<br>
|
||||
* {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}<p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Whether this version of Java is at most the version of Java passed in.</p>
|
||||
*
|
||||
* <p>For example:<br>
|
||||
* {@code myVersion.atMost(JavaVersion.JAVA_1_4)}<p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public boolean atMost(final JavaVersion requiredVersion) {
|
||||
return this.value <= requiredVersion.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given string with a Java version number to the
|
||||
* corresponding constant of this enumeration class. This method is used
|
||||
* internally.
|
||||
*
|
||||
* @param nom the Java version as string
|
||||
* @return the corresponding enumeration constant or <b>null</b> if the
|
||||
* version is unknown
|
||||
*/
|
||||
// helper for static importing
|
||||
static JavaVersion getJavaVersion(final String nom) {
|
||||
return get(nom);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <b>null</b> if the
|
||||
* version is unknown
|
||||
*/
|
||||
static JavaVersion get(final String versionStr) {
|
||||
if (versionStr == null) {
|
||||
return null;
|
||||
}
|
||||
switch (versionStr) {
|
||||
case "0.9":
|
||||
return JAVA_0_9;
|
||||
case "1.1":
|
||||
return JAVA_1_1;
|
||||
case "1.2":
|
||||
return JAVA_1_2;
|
||||
case "1.3":
|
||||
return JAVA_1_3;
|
||||
case "1.4":
|
||||
return JAVA_1_4;
|
||||
case "1.5":
|
||||
return JAVA_1_5;
|
||||
case "1.6":
|
||||
return JAVA_1_6;
|
||||
case "1.7":
|
||||
return JAVA_1_7;
|
||||
case "1.8":
|
||||
return JAVA_1_8;
|
||||
case "9":
|
||||
return JAVA_9;
|
||||
case "10":
|
||||
return JAVA_10;
|
||||
case "11":
|
||||
return JAVA_11;
|
||||
case "12":
|
||||
return JAVA_12;
|
||||
case "13":
|
||||
return JAVA_13;
|
||||
case "14":
|
||||
return JAVA_14;
|
||||
case "15":
|
||||
return JAVA_15;
|
||||
case "16":
|
||||
return JAVA_16;
|
||||
case "17":
|
||||
return JAVA_17;
|
||||
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(','));
|
||||
final int end = Math.max(versionStr.length(), versionStr.indexOf(',', firstComma));
|
||||
if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) {
|
||||
return JAVA_RECENT;
|
||||
}
|
||||
} else if (v > 10) {
|
||||
return JAVA_RECENT;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>The string value is overridden to return the standard name.</p>
|
||||
*
|
||||
* <p>For example, {@code "1.5"}.</p>
|
||||
*
|
||||
* @return the name, not null
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static float maxVersion() {
|
||||
final float v = toFloatVersion(System.getProperty("java.specification.version", "99.0"));
|
||||
if (v > 0) {
|
||||
return v;
|
||||
}
|
||||
return 99f;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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 {
|
||||
return NumberUtils.toFloat(value, defaultReturnValue);
|
||||
}
|
||||
return defaultReturnValue;
|
||||
}
|
||||
}
|
361
before/src/main/java/org/apache/commons/lang3/LocaleUtils.java
Normal file
361
before/src/main/java/org/apache/commons/lang3/LocaleUtils.java
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* <p>Operations to assist when working with a {@link Locale}.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
public class LocaleUtils {
|
||||
|
||||
// class to avoid synchronization (Init on demand)
|
||||
static class SyncAvoid {
|
||||
/** Unmodifiable list of available locales. */
|
||||
private static final List<Locale> AVAILABLE_LOCALE_LIST;
|
||||
/** Unmodifiable set of available locales. */
|
||||
private static final Set<Locale> AVAILABLE_LOCALE_SET;
|
||||
|
||||
static {
|
||||
final List<Locale> list = new ArrayList<>(Arrays.asList(Locale.getAvailableLocales())); // extra safe
|
||||
AVAILABLE_LOCALE_LIST = Collections.unmodifiableList(list);
|
||||
AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet<>(list));
|
||||
}
|
||||
}
|
||||
|
||||
/** Concurrent map of language locales by country. */
|
||||
private static final ConcurrentMap<String, List<Locale>> cLanguagesByCountry =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
/** Concurrent map of country locales by language. */
|
||||
private static final ConcurrentMap<String, List<Locale>> cCountriesByLanguage =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* <p>Obtains an unmodifiable list of installed locales.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @return the unmodifiable list of available locales
|
||||
*/
|
||||
public static List<Locale> availableLocaleList() {
|
||||
return SyncAvoid.AVAILABLE_LOCALE_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains an unmodifiable set of installed locales.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @return the unmodifiable set of available locales
|
||||
*/
|
||||
public static Set<Locale> availableLocaleSet() {
|
||||
return SyncAvoid.AVAILABLE_LOCALE_SET;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the list of countries supported for a given language.</p>
|
||||
*
|
||||
* <p>This method takes a language code and searches to find the
|
||||
* countries available for that language. Variant locales are removed.</p>
|
||||
*
|
||||
* @param languageCode the 2 letter language code, null returns empty
|
||||
* @return an unmodifiable List of Locale objects, not null
|
||||
*/
|
||||
public static List<Locale> countriesByLanguage(final String languageCode) {
|
||||
if (languageCode == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Locale> countries = cCountriesByLanguage.get(languageCode);
|
||||
if (countries == null) {
|
||||
countries = new ArrayList<>();
|
||||
final List<Locale> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if the locale specified is in the list of available locales.</p>
|
||||
*
|
||||
* @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 availableLocaleList().contains(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks 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) && (str.length() == 2 || str.length() == 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the list of languages supported for a given country.</p>
|
||||
*
|
||||
* <p>This method takes a country code and searches to find the
|
||||
* languages available for that country. Variant locales are removed.</p>
|
||||
*
|
||||
* @param countryCode the 2 letter country code, null returns empty
|
||||
* @return an unmodifiable List of Locale objects, not null
|
||||
*/
|
||||
public static List<Locale> languagesByCountry(final String countryCode) {
|
||||
if (countryCode == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Locale> langs = cLanguagesByCountry.get(countryCode);
|
||||
if (langs == null) {
|
||||
langs = new ArrayList<>();
|
||||
final List<Locale> locales = availableLocaleList();
|
||||
for (final Locale locale : locales) {
|
||||
if (countryCode.equals(locale.getCountry()) &&
|
||||
locale.getVariant().isEmpty()) {
|
||||
langs.add(locale);
|
||||
}
|
||||
}
|
||||
langs = Collections.unmodifiableList(langs);
|
||||
cLanguagesByCountry.putIfAbsent(countryCode, langs);
|
||||
langs = cLanguagesByCountry.get(countryCode);
|
||||
}
|
||||
return langs;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the list of locales to search through when performing
|
||||
* a locale search.</p>
|
||||
*
|
||||
* <pre>
|
||||
* localeLookupList(Locale("fr", "CA", "xxx"))
|
||||
* = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr")]
|
||||
* </pre>
|
||||
*
|
||||
* @param locale the locale to start from
|
||||
* @return the unmodifiable list of Locale objects, 0 being locale, not null
|
||||
*/
|
||||
public static List<Locale> localeLookupList(final Locale locale) {
|
||||
return localeLookupList(locale, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains the list of locales to search through when performing
|
||||
* a locale search.</p>
|
||||
*
|
||||
* <pre>
|
||||
* localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
|
||||
* = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr"), Locale("en"]
|
||||
* </pre>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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<Locale> localeLookupList(final Locale locale, final Locale defaultLocale) {
|
||||
final List<Locale> list = new ArrayList<>(4);
|
||||
if (locale != null) {
|
||||
list.add(locale);
|
||||
if (!locale.getVariant().isEmpty()) {
|
||||
list.add(new Locale(locale.getLanguage(), locale.getCountry()));
|
||||
}
|
||||
if (!locale.getCountry().isEmpty()) {
|
||||
list.add(new Locale(locale.getLanguage(), StringUtils.EMPTY));
|
||||
}
|
||||
if (!list.contains(defaultLocale)) {
|
||||
list.add(defaultLocale);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse a locale from the given String.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
private static Locale parseLocale(final String str) {
|
||||
if (isISO639LanguageCode(str)) {
|
||||
return new Locale(str);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given locale if non-{@code null}, otherwise {@link Locale#getDefault()}.
|
||||
*
|
||||
* @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 Locale toLocale(final Locale locale) {
|
||||
return locale != null ? locale : Locale.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts a String to a Locale.</p>
|
||||
*
|
||||
* <p>This method takes the string format of a locale and creates the
|
||||
* locale object from it.</p>
|
||||
*
|
||||
* <pre>
|
||||
* 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") (#)
|
||||
* </pre>
|
||||
*
|
||||
* <p>(#) 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.</p>
|
||||
*
|
||||
* <p>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.
|
||||
* </p>
|
||||
*
|
||||
* @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)
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
return parseLocale(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>{@code LocaleUtils} instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as {@code LocaleUtils.toLocale("en_GB");}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||
* to operate.</p>
|
||||
*/
|
||||
public LocaleUtils() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>{@code 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. </p>
|
||||
*
|
||||
* <pre>
|
||||
* public void foo() {
|
||||
* try {
|
||||
* // do something that throws an Exception
|
||||
* } catch (Exception ex) {
|
||||
* // don't know what to do here yet
|
||||
* throw new NotImplementedException("TODO", ex);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* This class was originally added in Lang 2.0, but removed in 3.0.
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public class NotImplementedException extends UnsupportedOperationException {
|
||||
|
||||
private static final long serialVersionUID = 20131021L;
|
||||
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* Constructs a NotImplementedException.
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
public NotImplementedException() {
|
||||
this.code = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a NotImplementedException.
|
||||
*
|
||||
* @param message description of the exception
|
||||
* @since 3.2
|
||||
*/
|
||||
public NotImplementedException(final String message) {
|
||||
this(message, (String) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a NotImplementedException.
|
||||
*
|
||||
* @param cause cause of the exception
|
||||
* @since 3.2
|
||||
*/
|
||||
public NotImplementedException(final Throwable cause) {
|
||||
this(cause, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a NotImplementedException.
|
||||
*
|
||||
* @param message description of the exception
|
||||
* @param cause cause of the exception
|
||||
* @since 3.2
|
||||
*/
|
||||
public NotImplementedException(final String message, final Throwable cause) {
|
||||
this(message, cause, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a NotImplementedException.
|
||||
*
|
||||
* @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 String message, final String code) {
|
||||
super(message);
|
||||
this.code = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the not implemented code. This is an unformatted piece of text intended to point to
|
||||
* 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
|
||||
*/
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
}
|
1350
before/src/main/java/org/apache/commons/lang3/ObjectUtils.java
Normal file
1350
before/src/main/java/org/apache/commons/lang3/ObjectUtils.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>Generates random {@code String}s.</p>
|
||||
*
|
||||
* <p><b>Caveat: Instances of {@link Random}, upon which the implementation of this
|
||||
* class relies, are not cryptographically secure.</b></p>
|
||||
*
|
||||
* <p>RandomStringUtils is intended for simple use cases. For more advanced
|
||||
* use cases consider using Apache Commons Text's
|
||||
* <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/RandomStringGenerator.html">
|
||||
* RandomStringGenerator</a> instead.</p>
|
||||
*
|
||||
* <p>The Apache Commons project provides
|
||||
* <a href="https://commons.apache.org/rng">Commons RNG</a> dedicated to pseudo-random number generation, that may be
|
||||
* a better choice for applications with more stringent requirements
|
||||
* (performance and/or correctness).</p>
|
||||
*
|
||||
* <p>Note that <em>private high surrogate</em> 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.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class RandomStringUtils {
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*/
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
/**
|
||||
* <p>{@code RandomStringUtils} instances should NOT be constructed in
|
||||
* standard programming. Instead, the class should be used as
|
||||
* {@code RandomStringUtils.random(5);}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||
* to operate.</p>
|
||||
*/
|
||||
public RandomStringUtils() {
|
||||
}
|
||||
|
||||
// Random
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of all characters.</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
*/
|
||||
public static String random(final int count) {
|
||||
return random(count, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of characters whose
|
||||
* ASCII value is between {@code 32} and {@code 126} (inclusive).</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
*/
|
||||
public static String randomAscii(final int count) {
|
||||
return random(count, 32, 127, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||
* the exclusive maximum.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of characters whose
|
||||
* ASCII value is between {@code 32} and {@code 126} (inclusive).</p>
|
||||
*
|
||||
* @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 randomAscii(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||
return randomAscii(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of Latin alphabetic
|
||||
* characters (a-z, A-Z).</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
*/
|
||||
public static String randomAlphabetic(final int count) {
|
||||
return random(count, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||
* the exclusive maximum.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z).</p>
|
||||
*
|
||||
* @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 randomAlphabetic(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||
return randomAlphabetic(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of Latin alphabetic
|
||||
* characters (a-z, A-Z) and the digits 0-9.</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
*/
|
||||
public static String randomAlphanumeric(final int count) {
|
||||
return random(count, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||
* the exclusive maximum.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of Latin alphabetic
|
||||
* characters (a-z, A-Z) and the digits 0-9.</p>
|
||||
*
|
||||
* @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 randomAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||
return randomAlphanumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters specified.</p>
|
||||
*
|
||||
* <p>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).</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
* @since 3.5
|
||||
*/
|
||||
public static String randomGraph(final int count) {
|
||||
return random(count, 33, 126, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||
* the exclusive maximum.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of \p{Graph} characters.</p>
|
||||
*
|
||||
* @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 randomGraph(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||
return randomGraph(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of numeric
|
||||
* characters.</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
*/
|
||||
public static String randomNumeric(final int count) {
|
||||
return random(count, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||
* the exclusive maximum.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of \p{Digit} characters.</p>
|
||||
*
|
||||
* @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 randomNumeric(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||
return randomNumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters specified.</p>
|
||||
*
|
||||
* <p>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).</p>
|
||||
*
|
||||
* @param count the length of random string to create
|
||||
* @return the random string
|
||||
* @since 3.5
|
||||
*/
|
||||
public static String randomPrint(final int count) {
|
||||
return random(count, 32, 126, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||
* the exclusive maximum.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of \p{Print} characters.</p>
|
||||
*
|
||||
* @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 randomPrint(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||
return randomPrint(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of alpha-numeric
|
||||
* characters as indicated by the arguments.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static String random(final int count, final boolean letters, final boolean numbers) {
|
||||
return random(count, 0, 0, letters, numbers);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of alpha-numeric
|
||||
* characters as indicated by the arguments.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string based on a variety of options, using
|
||||
* default source of randomness.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string based on a variety of options, using
|
||||
* supplied source of randomness.</p>
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>If set is not {@code null}, characters between start and
|
||||
* end are chosen.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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 numbers if {@code true}, generated string may include
|
||||
* numeric 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 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");
|
||||
}
|
||||
|
||||
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 + ")");
|
||||
}
|
||||
|
||||
final int zero_digit_ascii = 48;
|
||||
final int first_letter_ascii = 65;
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder(count);
|
||||
final int gap = end - start;
|
||||
|
||||
while (count-- != 0) {
|
||||
final int codePoint;
|
||||
if (chars == null) {
|
||||
codePoint = random.nextInt(gap) + start;
|
||||
|
||||
switch (Character.getType(codePoint)) {
|
||||
case Character.UNASSIGNED:
|
||||
case Character.PRIVATE_USE:
|
||||
case Character.SURROGATE:
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
codePoint = chars[random.nextInt(gap) + start];
|
||||
}
|
||||
|
||||
final int numberOfChars = Character.charCount(codePoint);
|
||||
if (count == 0 && numberOfChars > 1) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (letters && Character.isLetter(codePoint)
|
||||
|| numbers && Character.isDigit(codePoint)
|
||||
|| !letters && !numbers) {
|
||||
builder.appendCodePoint(codePoint);
|
||||
|
||||
if (numberOfChars == 2) {
|
||||
count--;
|
||||
}
|
||||
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a random string whose length is the number of characters
|
||||
* specified.</p>
|
||||
*
|
||||
* <p>Characters will be chosen from the set of characters specified.</p>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
254
before/src/main/java/org/apache/commons/lang3/RandomUtils.java
Normal file
254
before/src/main/java/org/apache/commons/lang3/RandomUtils.java
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>Utility library that supplements the standard {@link Random} class.</p>
|
||||
*
|
||||
* <p>Caveat: Instances of {@link Random} are not cryptographically secure.</p>
|
||||
*
|
||||
* <p>Please note that the Apache Commons project provides a component
|
||||
* dedicated to pseudo-random number generation, namely
|
||||
* <a href="https://commons.apache.org/rng">Commons RNG</a>, that may be
|
||||
* a better choice for applications with more stringent requirements
|
||||
* (performance and/or correctness).</p>
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@code RandomUtils} instances should NOT be constructed in standard
|
||||
* programming. Instead, the class should be used as
|
||||
* {@code RandomUtils.nextBytes(5);}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.
|
||||
* </p>
|
||||
*/
|
||||
public RandomUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a random boolean value
|
||||
* </p>
|
||||
*
|
||||
* @return the random boolean
|
||||
* @since 3.5
|
||||
*/
|
||||
public static boolean nextBoolean() {
|
||||
return RANDOM.nextBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates an array of random bytes.
|
||||
* </p>
|
||||
*
|
||||
* @param count
|
||||
* the size of the returned array
|
||||
* @return the random byte array
|
||||
* @throws IllegalArgumentException if {@code count} is negative
|
||||
*/
|
||||
public static byte[] nextBytes(final int count) {
|
||||
Validate.isTrue(count >= 0, "Count cannot be negative.");
|
||||
|
||||
final byte[] result = new byte[count];
|
||||
RANDOM.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a random integer within the specified range.
|
||||
* </p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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.");
|
||||
|
||||
if (startInclusive == endExclusive) {
|
||||
return startInclusive;
|
||||
}
|
||||
|
||||
return startInclusive + RANDOM.nextInt(endExclusive - startInclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Returns a random int within 0 - Integer.MAX_VALUE </p>
|
||||
*
|
||||
* @return the random integer
|
||||
* @see #nextInt(int, int)
|
||||
* @since 3.5
|
||||
*/
|
||||
public static int nextInt() {
|
||||
return nextInt(0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a random long within the specified range.
|
||||
* </p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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 startInclusive + nextLong(endExclusive - startInclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Returns a random long within 0 - Long.MAX_VALUE </p>
|
||||
*
|
||||
* @return the random long
|
||||
* @see #nextLong(long, long)
|
||||
* @since 3.5
|
||||
*/
|
||||
public static long nextLong() {
|
||||
return nextLong(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code long} value between 0 (inclusive) and the specified
|
||||
* value (exclusive).
|
||||
*
|
||||
* @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).
|
||||
*/
|
||||
private static long nextLong(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a random double within the specified range.
|
||||
* </p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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.");
|
||||
|
||||
if (startInclusive == endExclusive) {
|
||||
return startInclusive;
|
||||
}
|
||||
|
||||
return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Returns a random double within 0 - Double.MAX_VALUE </p>
|
||||
*
|
||||
* @return the random double
|
||||
* @see #nextDouble(double, double)
|
||||
* @since 3.5
|
||||
*/
|
||||
public static double nextDouble() {
|
||||
return nextDouble(0, Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a random float within the specified range.
|
||||
* </p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static float nextFloat(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());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Returns a random float within 0 - Float.MAX_VALUE </p>
|
||||
*
|
||||
* @return the random float
|
||||
* @see #nextFloat(float, float)
|
||||
* @since 3.5
|
||||
*/
|
||||
public static float nextFloat() {
|
||||
return nextFloat(0, Float.MAX_VALUE);
|
||||
}
|
||||
}
|
512
before/src/main/java/org/apache/commons/lang3/Range.java
Normal file
512
before/src/main/java/org/apache/commons/lang3/Range.java
Normal file
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* <p>An immutable range of objects from a minimum to maximum point inclusive.</p>
|
||||
*
|
||||
* <p>The objects need to either be implementations of {@code Comparable}
|
||||
* or you need to supply a {@code Comparator}. </p>
|
||||
*
|
||||
* <p>#ThreadSafe# if the objects and comparator are thread-safe</p>
|
||||
*
|
||||
* @param <T> The type of range values.
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class Range<T> 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
|
||||
*/
|
||||
@Override
|
||||
public int compare(final Object obj1, final Object obj2) {
|
||||
return ((Comparable) obj1).compareTo(obj2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization version.
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* <p>Obtains a range with the specified minimum and maximum values (both inclusive).</p>
|
||||
*
|
||||
* <p>The range uses the natural ordering of the elements to determine where
|
||||
* values lie in the range.</p>
|
||||
*
|
||||
* <p>The arguments may be passed in the order (min,max) or (max,min).
|
||||
* The getMinimum and getMaximum methods will return the correct values.</p>
|
||||
*
|
||||
* @param <T> 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}
|
||||
*/
|
||||
public static <T extends Comparable<T>> Range<T> between(final T fromInclusive, final T toInclusive) {
|
||||
return between(fromInclusive, toInclusive, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains a range with the specified minimum and maximum values (both inclusive).</p>
|
||||
*
|
||||
* <p>The range uses the specified {@code Comparator} to determine where
|
||||
* values lie in the range.</p>
|
||||
*
|
||||
* <p>The arguments may be passed in the order (min,max) or (max,min).
|
||||
* The getMinimum and getMaximum methods will return the correct values.</p>
|
||||
*
|
||||
* @param <T> 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}
|
||||
*/
|
||||
public static <T> Range<T> between(final T fromInclusive, final T toInclusive, final Comparator<T> comparator) {
|
||||
return new Range<>(fromInclusive, toInclusive, comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains a range using the specified element as both the minimum
|
||||
* and maximum in this range.</p>
|
||||
*
|
||||
* <p>The range uses the natural ordering of the elements to determine where
|
||||
* values lie in the range.</p>
|
||||
*
|
||||
* @param <T> 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}
|
||||
*/
|
||||
public static <T extends Comparable<T>> Range<T> is(final T element) {
|
||||
return between(element, element, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Obtains a range using the specified element as both the minimum
|
||||
* and maximum in this range.</p>
|
||||
*
|
||||
* <p>The range uses the specified {@code Comparator} to determine where
|
||||
* values lie in the range.</p>
|
||||
*
|
||||
* @param <T> 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}
|
||||
*/
|
||||
public static <T> Range<T> is(final T element, final Comparator<T> comparator) {
|
||||
return between(element, element, comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* The ordering scheme used in this range.
|
||||
*/
|
||||
private final Comparator<T> comparator;
|
||||
|
||||
/**
|
||||
* Cached output hashCode (class is immutable).
|
||||
*/
|
||||
private transient int hashCode;
|
||||
|
||||
/**
|
||||
* The maximum value in this range (inclusive).
|
||||
*/
|
||||
private final T maximum;
|
||||
|
||||
/**
|
||||
* The minimum value in this range (inclusive).
|
||||
*/
|
||||
private final T minimum;
|
||||
|
||||
/**
|
||||
* Cached output toString (class is immutable).
|
||||
*/
|
||||
private transient String toString;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Range(final T element1, final T element2, final Comparator<T> comp) {
|
||||
if (element1 == null || element2 == null) {
|
||||
throw new IllegalArgumentException("Elements in a range must not be null: element1=" +
|
||||
element1 + ", element2=" + element2);
|
||||
}
|
||||
if (comp == null) {
|
||||
this.comparator = ComparableComparator.INSTANCE;
|
||||
} else {
|
||||
this.comparator = comp;
|
||||
}
|
||||
if (this.comparator.compare(element1, element2) < 1) {
|
||||
this.minimum = element1;
|
||||
this.maximum = element2;
|
||||
} else {
|
||||
this.minimum = element2;
|
||||
this.maximum = element1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether the specified element occurs within this range.</p>
|
||||
*
|
||||
* @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) {
|
||||
return false;
|
||||
}
|
||||
return comparator.compare(element, minimum) > -1 && comparator.compare(element, maximum) < 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range contains all the elements of the specified range.</p>
|
||||
*
|
||||
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||
*
|
||||
* @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<T> otherRange) {
|
||||
if (otherRange == null) {
|
||||
return false;
|
||||
}
|
||||
return contains(otherRange.minimum)
|
||||
&& contains(otherRange.maximum);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks where the specified element occurs relative to this range.</p>
|
||||
*
|
||||
* <p>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. </p>
|
||||
*
|
||||
* @param element the element to check for, not null
|
||||
* @return -1, 0 or +1 depending on the element's location relative to the range
|
||||
*/
|
||||
public int elementCompareTo(final T element) {
|
||||
// Comparable API says throw NPE on null
|
||||
Validate.notNull(element, "element");
|
||||
if (isAfter(element)) {
|
||||
return -1;
|
||||
} else if (isBefore(element)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Element tests
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Compares this range to another object to test if they are equal.</p>.
|
||||
*
|
||||
* <p>To be equal, the minimum and maximum values must be equal, which
|
||||
* ignores any differences in the comparator.</p>
|
||||
*
|
||||
* @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()) {
|
||||
return false;
|
||||
} else {
|
||||
@SuppressWarnings("unchecked") // OK because we checked the class above
|
||||
final
|
||||
Range<T> range = (Range<T>) obj;
|
||||
return minimum.equals(range.minimum) &&
|
||||
maximum.equals(range.maximum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the comparator being used to determine if objects are within the range.</p>
|
||||
*
|
||||
* <p>Natural ordering uses an internal comparator implementation, thus this
|
||||
* method never returns null. See {@link #isNaturalOrdering()}.</p>
|
||||
*
|
||||
* @return the comparator being used, not null
|
||||
*/
|
||||
public Comparator<T> getComparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the maximum value in this range.</p>
|
||||
*
|
||||
* @return the maximum value in this range, not null
|
||||
*/
|
||||
public T getMaximum() {
|
||||
return maximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the minimum value in this range.</p>
|
||||
*
|
||||
* @return the minimum value in this range, not null
|
||||
*/
|
||||
public T getMinimum() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a suitable hash code for the range.</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public Range<T> intersectionWith(final Range<T> other) {
|
||||
if (!this.isOverlappedBy(other)) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot calculate intersection with non-overlapping range %s", other));
|
||||
}
|
||||
if (this.equals(other)) {
|
||||
return this;
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range is after the specified element.</p>
|
||||
*
|
||||
* @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) {
|
||||
return false;
|
||||
}
|
||||
return comparator.compare(element, minimum) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range is completely after the specified range.</p>
|
||||
*
|
||||
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||
*
|
||||
* @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<T> otherRange) {
|
||||
if (otherRange == null) {
|
||||
return false;
|
||||
}
|
||||
return isAfter(otherRange.maximum);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range is before the specified element.</p>
|
||||
*
|
||||
* @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) {
|
||||
return false;
|
||||
}
|
||||
return comparator.compare(element, maximum) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range is completely before the specified range.</p>
|
||||
*
|
||||
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||
*
|
||||
* @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<T> otherRange) {
|
||||
if (otherRange == null) {
|
||||
return false;
|
||||
}
|
||||
return isBefore(otherRange.minimum);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range ends with the specified element.</p>
|
||||
*
|
||||
* @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) {
|
||||
return false;
|
||||
}
|
||||
return comparator.compare(element, maximum) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Whether or not the Range is using the natural ordering of the elements.</p>
|
||||
*
|
||||
* <p>Natural ordering uses an internal comparator implementation, thus this
|
||||
* method is the only way to check if a null comparator was specified.</p>
|
||||
*
|
||||
* @return true if using natural ordering
|
||||
*/
|
||||
public boolean isNaturalOrdering() {
|
||||
return comparator == ComparableComparator.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range is overlapped by the specified range.</p>
|
||||
*
|
||||
* <p>Two ranges overlap if there is at least one element in common.</p>
|
||||
*
|
||||
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public boolean isOverlappedBy(final Range<T> otherRange) {
|
||||
if (otherRange == null) {
|
||||
return false;
|
||||
}
|
||||
return otherRange.contains(minimum)
|
||||
|| otherRange.contains(maximum)
|
||||
|| contains(otherRange.minimum);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether this range starts with the specified element.</p>
|
||||
*
|
||||
* @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) {
|
||||
return false;
|
||||
}
|
||||
return comparator.compare(element, minimum) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <pre>
|
||||
* 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
|
||||
* </pre>
|
||||
* @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
|
||||
*/
|
||||
public T fit(final T element) {
|
||||
// Comparable API says throw NPE on null
|
||||
Validate.notNull(element, "element");
|
||||
if (isAfter(element)) {
|
||||
return minimum;
|
||||
} else if (isBefore(element)) {
|
||||
return maximum;
|
||||
} else {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the range as a {@code String}.</p>
|
||||
*
|
||||
* <p>The format of the String is '[<i>min</i>..<i>max</i>]'.</p>
|
||||
*
|
||||
* @return the {@code String} representation of this range
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (toString == null) {
|
||||
toString = "[" + minimum + ".." + maximum + "]";
|
||||
}
|
||||
return toString;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Formats the receiver using the given format.</p>
|
||||
*
|
||||
* <p>This uses {@link java.util.Formattable} to perform the formatting. Three variables may
|
||||
* be used to embed the minimum, maximum and comparator.
|
||||
* Use {@code %1$s} for the minimum element, {@code %2$s} for the maximum element
|
||||
* and {@code %3$s} for the comparator.
|
||||
* The default format used by {@code toString()} is {@code [%1$s..%2$s]}.</p>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
458
before/src/main/java/org/apache/commons/lang3/RegExUtils.java
Normal file
458
before/src/main/java/org/apache/commons/lang3/RegExUtils.java
Normal file
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>Helpers to process Strings using regular expressions.</p>
|
||||
* @see java.util.regex.Pattern
|
||||
* @since 3.8
|
||||
*/
|
||||
public class RegExUtils {
|
||||
|
||||
/**
|
||||
* <p>Removes each substring of the text String that matches the given regular expression pattern.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code pattern.matcher(text).replaceAll(StringUtils.EMPTY)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.removeAll(null, *) = null
|
||||
* StringUtils.removeAll("any", (Pattern) null) = "any"
|
||||
* StringUtils.removeAll("any", Pattern.compile("")) = "any"
|
||||
* StringUtils.removeAll("any", Pattern.compile(".*")) = ""
|
||||
* StringUtils.removeAll("any", Pattern.compile(".+")) = ""
|
||||
* StringUtils.removeAll("abc", Pattern.compile(".?")) = ""
|
||||
* StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>")) = "A\nB"
|
||||
* StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>")) = "AB"
|
||||
* StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL)) = "AB"
|
||||
* StringUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]")) = "ABC123"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to remove from, may be null
|
||||
* @param regex the regular expression to which this string is to be matched
|
||||
* @return the text with any removes processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @see #replaceAll(String, Pattern, String)
|
||||
* @see java.util.regex.Matcher#replaceAll(String)
|
||||
* @see java.util.regex.Pattern
|
||||
*/
|
||||
public static String removeAll(final String text, final Pattern regex) {
|
||||
return replaceAll(text, regex, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Removes each substring of the text String that matches the given regular expression.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code text.replaceAll(regex, StringUtils.EMPTY)}</li>
|
||||
* <li>{@code Pattern.compile(regex).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <p>Unlike in the {@link #removePattern(String, String)} method, the {@link Pattern#DOTALL} option
|
||||
* is NOT automatically added.
|
||||
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||
* DOTALL is also known as single-line mode in Perl.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.removeAll(null, *) = null
|
||||
* StringUtils.removeAll("any", (String) null) = "any"
|
||||
* StringUtils.removeAll("any", "") = "any"
|
||||
* StringUtils.removeAll("any", ".*") = ""
|
||||
* StringUtils.removeAll("any", ".+") = ""
|
||||
* StringUtils.removeAll("abc", ".?") = ""
|
||||
* StringUtils.removeAll("A<__>\n<__>B", "<.*>") = "A\nB"
|
||||
* StringUtils.removeAll("A<__>\n<__>B", "(?s)<.*>") = "AB"
|
||||
* StringUtils.removeAll("ABCabc123abc", "[a-z]") = "ABC123"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to remove from, may be null
|
||||
* @param regex the regular expression to which this string is to be matched
|
||||
* @return the text with any removes processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @throws java.util.regex.PatternSyntaxException
|
||||
* if the regular expression's syntax is invalid
|
||||
*
|
||||
* @see #replaceAll(String, String, String)
|
||||
* @see #removePattern(String, String)
|
||||
* @see String#replaceAll(String, String)
|
||||
* @see java.util.regex.Pattern
|
||||
* @see java.util.regex.Pattern#DOTALL
|
||||
*/
|
||||
public static String removeAll(final String text, final String regex) {
|
||||
return replaceAll(text, regex, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Removes the first substring of the text string that matches the given regular expression pattern.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.removeFirst(null, *) = null
|
||||
* StringUtils.removeFirst("any", (Pattern) null) = "any"
|
||||
* StringUtils.removeFirst("any", Pattern.compile("")) = "any"
|
||||
* StringUtils.removeFirst("any", Pattern.compile(".*")) = ""
|
||||
* StringUtils.removeFirst("any", Pattern.compile(".+")) = ""
|
||||
* StringUtils.removeFirst("abc", Pattern.compile(".?")) = "bc"
|
||||
* StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>")) = "A\n<__>B"
|
||||
* StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>")) = "AB"
|
||||
* StringUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]")) = "ABCbc123"
|
||||
* StringUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+")) = "ABC123abc"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to remove from, may be null
|
||||
* @param regex the regular expression pattern to which this string is to be matched
|
||||
* @return the text with the first replacement processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @see #replaceFirst(String, Pattern, String)
|
||||
* @see java.util.regex.Matcher#replaceFirst(String)
|
||||
* @see java.util.regex.Pattern
|
||||
*/
|
||||
public static String removeFirst(final String text, final Pattern regex) {
|
||||
return replaceFirst(text, regex, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Removes the first substring of the text string that matches the given regular expression.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code text.replaceFirst(regex, StringUtils.EMPTY)}</li>
|
||||
* <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <p>The {@link Pattern#DOTALL} option is NOT automatically added.
|
||||
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||
* DOTALL is also known as single-line mode in Perl.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.removeFirst(null, *) = null
|
||||
* StringUtils.removeFirst("any", (String) null) = "any"
|
||||
* StringUtils.removeFirst("any", "") = "any"
|
||||
* StringUtils.removeFirst("any", ".*") = ""
|
||||
* StringUtils.removeFirst("any", ".+") = ""
|
||||
* StringUtils.removeFirst("abc", ".?") = "bc"
|
||||
* StringUtils.removeFirst("A<__>\n<__>B", "<.*>") = "A\n<__>B"
|
||||
* StringUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>") = "AB"
|
||||
* StringUtils.removeFirst("ABCabc123", "[a-z]") = "ABCbc123"
|
||||
* StringUtils.removeFirst("ABCabc123abc", "[a-z]+") = "ABC123abc"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to remove from, may be null
|
||||
* @param regex the regular expression to which this string is to be matched
|
||||
* @return the text with the first replacement processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @throws java.util.regex.PatternSyntaxException
|
||||
* if the regular expression's syntax is invalid
|
||||
*
|
||||
* @see #replaceFirst(String, String, String)
|
||||
* @see String#replaceFirst(String, String)
|
||||
* @see java.util.regex.Pattern
|
||||
* @see java.util.regex.Pattern#DOTALL
|
||||
*/
|
||||
public static String removeFirst(final String text, final String regex) {
|
||||
return replaceFirst(text, regex, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Removes each substring of the source String that matches the given regular expression using the DOTALL option.</p>
|
||||
*
|
||||
* This call is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code text.replaceAll("(?s)" + regex, StringUtils.EMPTY)}</li>
|
||||
* <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.removePattern(null, *) = null
|
||||
* StringUtils.removePattern("any", (String) null) = "any"
|
||||
* StringUtils.removePattern("A<__>\n<__>B", "<.*>") = "AB"
|
||||
* StringUtils.removePattern("ABCabc123", "[a-z]") = "ABC123"
|
||||
* </pre>
|
||||
*
|
||||
* @param text
|
||||
* the source string
|
||||
* @param regex
|
||||
* the regular expression to which this string is to be matched
|
||||
* @return The resulting {@code String}
|
||||
* @see #replacePattern(String, String, String)
|
||||
* @see String#replaceAll(String, String)
|
||||
* @see Pattern#DOTALL
|
||||
*/
|
||||
public static String removePattern(final String text, final String regex) {
|
||||
return replacePattern(text, regex, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceAll(null, *, *) = null
|
||||
* StringUtils.replaceAll("any", (Pattern) null, *) = "any"
|
||||
* StringUtils.replaceAll("any", *, null) = "any"
|
||||
* StringUtils.replaceAll("", Pattern.compile(""), "zzz") = "zzz"
|
||||
* StringUtils.replaceAll("", Pattern.compile(".*"), "zzz") = "zzz"
|
||||
* StringUtils.replaceAll("", Pattern.compile(".+"), "zzz") = ""
|
||||
* StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ") = "ZZaZZbZZcZZ"
|
||||
* StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z") = "z\nz"
|
||||
* StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
|
||||
* StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z") = "z"
|
||||
* StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_") = "ABC___123"
|
||||
* StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_") = "ABC_123"
|
||||
* StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "") = "ABC123"
|
||||
* StringUtils.replaceAll("Lorem ipsum dolor sit", Pattern.compile("( +)([a-z]+)"), "_$2") = "Lorem_ipsum_dolor_sit"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to search and replace in, may be null
|
||||
* @param regex the regular expression pattern to which this string is to be matched
|
||||
* @param replacement the string to be substituted for each match
|
||||
* @return the text with any replacements processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @see java.util.regex.Matcher#replaceAll(String)
|
||||
* @see java.util.regex.Pattern
|
||||
*/
|
||||
public static String replaceAll(final String text, final Pattern regex, final String replacement) {
|
||||
if (ObjectUtils.anyNull(text, regex, replacement)) {
|
||||
return text;
|
||||
}
|
||||
return regex.matcher(text).replaceAll(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces each substring of the text String that matches the given regular expression
|
||||
* with the given replacement.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code text.replaceAll(regex, replacement)}</li>
|
||||
* <li>{@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <p>Unlike in the {@link #replacePattern(String, String, String)} method, the {@link Pattern#DOTALL} option
|
||||
* is NOT automatically added.
|
||||
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||
* DOTALL is also known as single-line mode in Perl.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceAll(null, *, *) = null
|
||||
* StringUtils.replaceAll("any", (String) null, *) = "any"
|
||||
* StringUtils.replaceAll("any", *, null) = "any"
|
||||
* StringUtils.replaceAll("", "", "zzz") = "zzz"
|
||||
* StringUtils.replaceAll("", ".*", "zzz") = "zzz"
|
||||
* StringUtils.replaceAll("", ".+", "zzz") = ""
|
||||
* StringUtils.replaceAll("abc", "", "ZZ") = "ZZaZZbZZcZZ"
|
||||
* StringUtils.replaceAll("<__>\n<__>", "<.*>", "z") = "z\nz"
|
||||
* StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z") = "z"
|
||||
* StringUtils.replaceAll("ABCabc123", "[a-z]", "_") = "ABC___123"
|
||||
* StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_") = "ABC_123"
|
||||
* StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "") = "ABC123"
|
||||
* StringUtils.replaceAll("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2") = "Lorem_ipsum_dolor_sit"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to search and replace in, may be null
|
||||
* @param regex the regular expression to which this string is to be matched
|
||||
* @param replacement the string to be substituted for each match
|
||||
* @return the text with any replacements processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @throws java.util.regex.PatternSyntaxException
|
||||
* if the regular expression's syntax is invalid
|
||||
*
|
||||
* @see #replacePattern(String, String, String)
|
||||
* @see String#replaceAll(String, String)
|
||||
* @see java.util.regex.Pattern
|
||||
* @see java.util.regex.Pattern#DOTALL
|
||||
*/
|
||||
public static String replaceAll(final String text, final String regex, final String replacement) {
|
||||
if (ObjectUtils.anyNull(text, regex, replacement)) {
|
||||
return text;
|
||||
}
|
||||
return text.replaceAll(regex, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces the first substring of the text string that matches the given regular expression pattern
|
||||
* with the given replacement.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceFirst(null, *, *) = null
|
||||
* StringUtils.replaceFirst("any", (Pattern) null, *) = "any"
|
||||
* StringUtils.replaceFirst("any", *, null) = "any"
|
||||
* StringUtils.replaceFirst("", Pattern.compile(""), "zzz") = "zzz"
|
||||
* StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz") = "zzz"
|
||||
* StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz") = ""
|
||||
* StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ") = "ZZabc"
|
||||
* StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z") = "z\n<__>"
|
||||
* StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z") = "z"
|
||||
* StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_") = "ABC_bc123"
|
||||
* StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_") = "ABC_123abc"
|
||||
* StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "") = "ABC123abc"
|
||||
* StringUtils.replaceFirst("Lorem ipsum dolor sit", Pattern.compile("( +)([a-z]+)"), "_$2") = "Lorem_ipsum dolor sit"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to search and replace in, may be null
|
||||
* @param regex the regular expression pattern to which this string is to be matched
|
||||
* @param replacement the string to be substituted for the first match
|
||||
* @return the text with the first replacement processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @see java.util.regex.Matcher#replaceFirst(String)
|
||||
* @see java.util.regex.Pattern
|
||||
*/
|
||||
public static String replaceFirst(final String text, final Pattern regex, final String replacement) {
|
||||
if (text == null || regex == null|| replacement == null ) {
|
||||
return text;
|
||||
}
|
||||
return regex.matcher(text).replaceFirst(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces the first substring of the text string that matches the given regular expression
|
||||
* with the given replacement.</p>
|
||||
*
|
||||
* This method is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code text.replaceFirst(regex, replacement)}</li>
|
||||
* <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <p>The {@link Pattern#DOTALL} option is NOT automatically added.
|
||||
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||
* DOTALL is also known as single-line mode in Perl.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceFirst(null, *, *) = null
|
||||
* StringUtils.replaceFirst("any", (String) null, *) = "any"
|
||||
* StringUtils.replaceFirst("any", *, null) = "any"
|
||||
* StringUtils.replaceFirst("", "", "zzz") = "zzz"
|
||||
* StringUtils.replaceFirst("", ".*", "zzz") = "zzz"
|
||||
* StringUtils.replaceFirst("", ".+", "zzz") = ""
|
||||
* StringUtils.replaceFirst("abc", "", "ZZ") = "ZZabc"
|
||||
* StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z") = "z\n<__>"
|
||||
* StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z") = "z"
|
||||
* StringUtils.replaceFirst("ABCabc123", "[a-z]", "_") = "ABC_bc123"
|
||||
* StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_") = "ABC_123abc"
|
||||
* StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "") = "ABC123abc"
|
||||
* StringUtils.replaceFirst("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2") = "Lorem_ipsum dolor sit"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to search and replace in, may be null
|
||||
* @param regex the regular expression to which this string is to be matched
|
||||
* @param replacement the string to be substituted for the first match
|
||||
* @return the text with the first replacement processed,
|
||||
* {@code null} if null String input
|
||||
*
|
||||
* @throws java.util.regex.PatternSyntaxException
|
||||
* if the regular expression's syntax is invalid
|
||||
*
|
||||
* @see String#replaceFirst(String, String)
|
||||
* @see java.util.regex.Pattern
|
||||
* @see java.util.regex.Pattern#DOTALL
|
||||
*/
|
||||
public static String replaceFirst(final String text, final String regex, final String replacement) {
|
||||
if (text == null || regex == null|| replacement == null ) {
|
||||
return text;
|
||||
}
|
||||
return text.replaceFirst(regex, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces each substring of the source String that matches the given regular expression with the given
|
||||
* replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.</p>
|
||||
*
|
||||
* This call is a {@code null} safe equivalent to:
|
||||
* <ul>
|
||||
* <li>{@code text.replaceAll("(?s)" + regex, replacement)}</li>
|
||||
* <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replacePattern(null, *, *) = null
|
||||
* StringUtils.replacePattern("any", (String) null, *) = "any"
|
||||
* StringUtils.replacePattern("any", *, null) = "any"
|
||||
* StringUtils.replacePattern("", "", "zzz") = "zzz"
|
||||
* StringUtils.replacePattern("", ".*", "zzz") = "zzz"
|
||||
* StringUtils.replacePattern("", ".+", "zzz") = ""
|
||||
* StringUtils.replacePattern("<__>\n<__>", "<.*>", "z") = "z"
|
||||
* StringUtils.replacePattern("ABCabc123", "[a-z]", "_") = "ABC___123"
|
||||
* StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_") = "ABC_123"
|
||||
* StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "") = "ABC123"
|
||||
* StringUtils.replacePattern("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2") = "Lorem_ipsum_dolor_sit"
|
||||
* </pre>
|
||||
*
|
||||
* @param text
|
||||
* the source string
|
||||
* @param regex
|
||||
* the regular expression to which this string is to be matched
|
||||
* @param replacement
|
||||
* the string to be substituted for each match
|
||||
* @return The resulting {@code String}
|
||||
* @see #replaceAll(String, String, String)
|
||||
* @see String#replaceAll(String, String)
|
||||
* @see Pattern#DOTALL
|
||||
*/
|
||||
public static String replacePattern(final String text, final String regex, final String replacement) {
|
||||
if (ObjectUtils.anyNull(text, regex, replacement)) {
|
||||
return text;
|
||||
}
|
||||
return Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
/**
|
||||
* <p>Exception thrown when the Serialization process fails.</p>
|
||||
*
|
||||
* <p>The original error is wrapped within this one.</p>
|
||||
*
|
||||
* <p>#NotThreadSafe# because Throwable is not thread-safe</p>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SerializationException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Required for serialization support.
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 4029025366392702726L;
|
||||
|
||||
/**
|
||||
* <p>Constructs a new {@code SerializationException} without specified
|
||||
* detail message.</p>
|
||||
*/
|
||||
public SerializationException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a new {@code SerializationException} with specified
|
||||
* detail message.</p>
|
||||
*
|
||||
* @param msg The error message.
|
||||
*/
|
||||
public SerializationException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a new {@code SerializationException} with specified
|
||||
* nested {@code Throwable}.</p>
|
||||
*
|
||||
* @param cause The {@code Exception} or {@code Error}
|
||||
* that caused this exception to be thrown.
|
||||
*/
|
||||
public SerializationException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a new {@code SerializationException} with specified
|
||||
* detail message and nested {@code Throwable}.</p>
|
||||
*
|
||||
* @param msg The error message.
|
||||
* @param cause The {@code Exception} or {@code Error}
|
||||
* that caused this exception to be thrown.
|
||||
*/
|
||||
public SerializationException(final String msg, final Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Assists with the serialization process and performs additional functionality based
|
||||
* on serialization.</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>Deep clone using serialization
|
||||
* <li>Serialize managing finally and IOException
|
||||
* <li>Deserialize managing finally and IOException
|
||||
* </ul>
|
||||
*
|
||||
* <p>This class throws exceptions for invalid {@code null} inputs.
|
||||
* Each method documents its behavior in more detail.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SerializationUtils {
|
||||
|
||||
/**
|
||||
* <p>Custom specialization of the standard JDK {@link java.io.ObjectInputStream}
|
||||
* that uses a custom {@code ClassLoader} to resolve a class.
|
||||
* If the specified {@code ClassLoader} is not able to resolve the class,
|
||||
* the context classloader of the current thread will be used.
|
||||
* This way, the standard deserialization work also in web-application
|
||||
* containers and application servers, no matter in which of the
|
||||
* {@code ClassLoader} the particular class that encapsulates
|
||||
* serialization/deserialization lives. </p>
|
||||
*
|
||||
* <p>For more in-depth information about the problem for which this
|
||||
* class here is a workaround, see the JIRA issue LANG-626. </p>
|
||||
*/
|
||||
static class ClassLoaderAwareObjectInputStream extends ObjectInputStream {
|
||||
private static final Map<String, Class<?>> primitiveTypes =
|
||||
new HashMap<>();
|
||||
|
||||
static {
|
||||
primitiveTypes.put("byte", byte.class);
|
||||
primitiveTypes.put("short", short.class);
|
||||
primitiveTypes.put("int", int.class);
|
||||
primitiveTypes.put("long", long.class);
|
||||
primitiveTypes.put("float", float.class);
|
||||
primitiveTypes.put("double", double.class);
|
||||
primitiveTypes.put("boolean", boolean.class);
|
||||
primitiveTypes.put("char", char.class);
|
||||
primitiveTypes.put("void", void.class);
|
||||
}
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param in The {@code InputStream}.
|
||||
* @param classLoader classloader to use
|
||||
* @throws IOException if an I/O error occurs while reading stream header.
|
||||
* @see java.io.ObjectInputStream
|
||||
*/
|
||||
ClassLoaderAwareObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
|
||||
super(in);
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden version that uses the parameterized {@code ClassLoader} or the {@code ClassLoader}
|
||||
* of the current {@code Thread} to resolve the class.
|
||||
* @param desc An instance of class {@code ObjectStreamClass}.
|
||||
* @return A {@code Class} object corresponding to {@code desc}.
|
||||
* @throws IOException Any of the usual Input/Output exceptions.
|
||||
* @throws ClassNotFoundException If class of a serialized object cannot be found.
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException {
|
||||
final String name = desc.getName();
|
||||
try {
|
||||
return Class.forName(name, false, classLoader);
|
||||
} catch (final ClassNotFoundException ex) {
|
||||
try {
|
||||
return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
|
||||
} catch (final ClassNotFoundException cnfe) {
|
||||
final Class<?> cls = primitiveTypes.get(name);
|
||||
if (cls != null) {
|
||||
return cls;
|
||||
}
|
||||
throw cnfe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deep clone an {@code Object} using serialization.</p>
|
||||
*
|
||||
* <p>This is many times slower than writing clone methods by hand
|
||||
* on all objects in your object graph. However, for complex object
|
||||
* graphs, or for those that don't support deep cloning this can
|
||||
* be a simple alternative implementation. Of course all the objects
|
||||
* must be {@code Serializable}.</p>
|
||||
*
|
||||
* @param <T> the type of the object involved
|
||||
* @param object the {@code Serializable} object to clone
|
||||
* @return the cloned object
|
||||
* @throws SerializationException (runtime) if the serialization fails
|
||||
*/
|
||||
public static <T extends Serializable> T clone(final T object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
final byte[] objectData = serialize(object);
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
|
||||
|
||||
try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais,
|
||||
object.getClass().getClassLoader())) {
|
||||
/*
|
||||
* when we serialize and deserialize an object,
|
||||
* it is reasonable to assume the deserialized object
|
||||
* is of the same type as the original serialized object
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // see above
|
||||
final T readObject = (T) in.readObject();
|
||||
return readObject;
|
||||
|
||||
} catch (final ClassNotFoundException ex) {
|
||||
throw new SerializationException("ClassNotFoundException while reading cloned object data", ex);
|
||||
} catch (final IOException ex) {
|
||||
throw new SerializationException("IOException while reading or closing cloned object data", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Deserializes a single {@code Object} from an array of bytes.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site.
|
||||
* Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException.
|
||||
* Note that in both cases, the ClassCastException is in the call site, not in this method.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the object type to be deserialized
|
||||
* @param objectData
|
||||
* the serialized object, must not be null
|
||||
* @return the deserialized object
|
||||
* @throws NullPointerException if {@code objectData} is {@code null}
|
||||
* @throws SerializationException (runtime) if the serialization fails
|
||||
*/
|
||||
public static <T> T deserialize(final byte[] objectData) {
|
||||
Validate.notNull(objectData, "objectData");
|
||||
return deserialize(new ByteArrayInputStream(objectData));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Deserializes an {@code Object} from the specified stream.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The stream will be closed once the object is written. This avoids the need for a finally clause, and maybe also
|
||||
* exception handling, in the application code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The stream passed in is not buffered internally within this method. This is the responsibility of your
|
||||
* application if desired.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site.
|
||||
* Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException.
|
||||
* Note that in both cases, the ClassCastException is in the call site, not in this method.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the object type to be deserialized
|
||||
* @param inputStream
|
||||
* the serialized object input stream, must not be null
|
||||
* @return the deserialized object
|
||||
* @throws NullPointerException if {@code inputStream} is {@code null}
|
||||
* @throws SerializationException (runtime) if the serialization fails
|
||||
*/
|
||||
@SuppressWarnings("resource") // inputStream is managed by the caller
|
||||
public static <T> T deserialize(final InputStream inputStream) {
|
||||
Validate.notNull(inputStream, "inputStream");
|
||||
try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final T obj = (T) in.readObject();
|
||||
return obj;
|
||||
} catch (final ClassNotFoundException | IOException ex) {
|
||||
throw new SerializationException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a serialization roundtrip. Serializes and deserializes the given object, great for testing objects that
|
||||
* implement {@link Serializable}.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object involved
|
||||
* @param obj
|
||||
* the object to roundtrip
|
||||
* @return the serialized and deserialized object
|
||||
* @since 3.3
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // OK, because we serialized a type `T`
|
||||
public static <T extends Serializable> T roundtrip(final T obj) {
|
||||
return (T) deserialize(serialize(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Serializes an {@code Object} to a byte array for
|
||||
* storage/serialization.</p>
|
||||
*
|
||||
* @param obj the object to serialize to bytes
|
||||
* @return a byte[] with the converted Serializable
|
||||
* @throws SerializationException (runtime) if the serialization fails
|
||||
*/
|
||||
public static byte[] serialize(final Serializable obj) {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
serialize(obj, baos);
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Serializes an {@code Object} to the specified stream.</p>
|
||||
*
|
||||
* <p>The stream will be closed once the object is written.
|
||||
* This avoids the need for a finally clause, and maybe also exception
|
||||
* handling, in the application code.</p>
|
||||
*
|
||||
* <p>The stream passed in is not buffered internally within this method.
|
||||
* This is the responsibility of your application if desired.</p>
|
||||
*
|
||||
* @param obj the object to serialize to bytes, may be null
|
||||
* @param outputStream the stream to write to, must not be null
|
||||
* @throws NullPointerException if {@code outputStream} is {@code null}
|
||||
* @throws SerializationException (runtime) if the serialization fails
|
||||
*/
|
||||
@SuppressWarnings("resource") // outputStream is managed by the caller
|
||||
public static void serialize(final Serializable obj, final OutputStream outputStream) {
|
||||
Validate.notNull(outputStream, "outputStream");
|
||||
try (ObjectOutputStream out = new ObjectOutputStream(outputStream)) {
|
||||
out.writeObject(obj);
|
||||
} catch (final IOException ex) {
|
||||
throw new SerializationException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>SerializationUtils instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as {@code SerializationUtils.clone(object)}.</p>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||
* to operate.</p>
|
||||
* @since 2.0
|
||||
*/
|
||||
public SerializationUtils() {
|
||||
}
|
||||
|
||||
}
|
507
before/src/main/java/org/apache/commons/lang3/Streams.java
Normal file
507
before/src/main/java/org/apache/commons/lang3/Streams.java
Normal file
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.Functions.FailableConsumer;
|
||||
import org.apache.commons.lang3.Functions.FailableFunction;
|
||||
import org.apache.commons.lang3.Functions.FailablePredicate;
|
||||
|
||||
/**
|
||||
* Provides utility functions, and classes for working with the
|
||||
* {@code java.util.stream} 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
|
||||
* <pre>
|
||||
* Consumer<java.lang.reflect.Method> consumer = m -> {
|
||||
* try {
|
||||
* m.invoke(o, args);
|
||||
* } catch (Throwable t) {
|
||||
* throw Functions.rethrow(t);
|
||||
* }
|
||||
* };
|
||||
* stream.forEach(consumer);
|
||||
* </pre>
|
||||
* Using a {@link FailableStream}, this can be rewritten as follows:
|
||||
* <pre>
|
||||
* Streams.failable(stream).forEach((m) -> m.invoke(o, args));
|
||||
* </pre>
|
||||
* Obviously, the second version is much more concise and the spirit of
|
||||
* Lambda expressions is met better than in the first version.
|
||||
*
|
||||
* @see Stream
|
||||
* @see Functions
|
||||
* @since 3.10
|
||||
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Streams {
|
||||
|
||||
/**
|
||||
* A reduced, and simplified version of a {@link Stream} with
|
||||
* failable method signatures.
|
||||
* @param <O> The streams element type.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams.FailableStream}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static class FailableStream<O extends Object> {
|
||||
|
||||
private Stream<O> stream;
|
||||
private boolean terminated;
|
||||
|
||||
/**
|
||||
* Constructs a new instance with the given {@code stream}.
|
||||
* @param stream The stream.
|
||||
*/
|
||||
public FailableStream(final Stream<O> stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
protected void assertNotTerminated() {
|
||||
if (terminated) {
|
||||
throw new IllegalStateException("This stream is already terminated.");
|
||||
}
|
||||
}
|
||||
|
||||
protected void makeTerminated() {
|
||||
assertNotTerminated();
|
||||
terminated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a FailableStream consisting of the elements of this stream that match
|
||||
* the given FailablePredicate.
|
||||
*
|
||||
* <p>This is an intermediate operation.
|
||||
*
|
||||
* @param predicate a non-interfering, stateless predicate to apply to each
|
||||
* element to determine if it should be included.
|
||||
* @return the new stream
|
||||
*/
|
||||
public FailableStream<O> filter(final FailablePredicate<O, ?> predicate){
|
||||
assertNotTerminated();
|
||||
stream = stream.filter(Functions.asPredicate(predicate));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an action for each element of this stream.
|
||||
*
|
||||
* <p>This is a terminal operation.
|
||||
*
|
||||
* <p>The behavior of this operation is explicitly nondeterministic.
|
||||
* For parallel stream pipelines, this operation does <em>not</em>
|
||||
* guarantee to respect the encounter order of the stream, as doing so
|
||||
* would sacrifice the benefit of parallelism. For any given element, the
|
||||
* action may be performed at whatever time and in whatever thread the
|
||||
* library chooses. If the action accesses shared state, it is
|
||||
* responsible for providing the required synchronization.
|
||||
*
|
||||
* @param action a non-interfering action to perform on the elements
|
||||
*/
|
||||
public void forEach(final FailableConsumer<O, ?> action) {
|
||||
makeTerminated();
|
||||
stream().forEach(Functions.asConsumer(action));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a mutable reduction operation on the elements of this stream using a
|
||||
* {@code Collector}. A {@code Collector}
|
||||
* encapsulates the functions used as arguments to
|
||||
* {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of
|
||||
* collection strategies and composition of collect operations such as
|
||||
* multiple-level grouping or partitioning.
|
||||
*
|
||||
* <p>If the underlying stream is parallel, and the {@code Collector}
|
||||
* is concurrent, and either the stream is unordered or the collector is
|
||||
* unordered, then a concurrent reduction will be performed
|
||||
* (see {@link Collector} for details on concurrent reduction.)
|
||||
*
|
||||
* <p>This is a terminal operation.
|
||||
*
|
||||
* <p>When executed in parallel, multiple intermediate results may be
|
||||
* instantiated, populated, and merged so as to maintain isolation of
|
||||
* mutable data structures. Therefore, even when executed in parallel
|
||||
* with non-thread-safe data structures (such as {@code ArrayList}), no
|
||||
* additional synchronization is needed for a parallel reduction.
|
||||
*
|
||||
* Note
|
||||
* The following will accumulate strings into an ArrayList:
|
||||
* <pre>{@code
|
||||
* List<String> asList = stringStream.collect(Collectors.toList());
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The following will classify {@code Person} objects by city:
|
||||
* <pre>{@code
|
||||
* Map<String, List<Person>> peopleByCity
|
||||
* = personStream.collect(Collectors.groupingBy(Person::getCity));
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The following will classify {@code Person} objects by state and city,
|
||||
* cascading two {@code Collector}s together:
|
||||
* <pre>{@code
|
||||
* Map<String, Map<String, List<Person>>> peopleByStateAndCity
|
||||
* = personStream.collect(Collectors.groupingBy(Person::getState,
|
||||
* Collectors.groupingBy(Person::getCity)));
|
||||
* }</pre>
|
||||
*
|
||||
* @param <R> the type of the result
|
||||
* @param <A> the intermediate accumulation type of the {@code Collector}
|
||||
* @param collector the {@code Collector} describing the reduction
|
||||
* @return the result of the reduction
|
||||
* @see #collect(Supplier, BiConsumer, BiConsumer)
|
||||
* @see Collectors
|
||||
*/
|
||||
public <A, R> R collect(final Collector<? super O, A, R> collector) {
|
||||
makeTerminated();
|
||||
return stream().collect(collector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a mutable reduction operation on the elements of this FailableStream.
|
||||
* A mutable reduction is one in which the reduced value is a mutable result
|
||||
* container, such as an {@code ArrayList}, and elements are incorporated by updating
|
||||
* the state of the result rather than by replacing the result. This produces a result equivalent to:
|
||||
* <pre>{@code
|
||||
* R result = supplier.get();
|
||||
* for (T element : this stream)
|
||||
* accumulator.accept(result, element);
|
||||
* return result;
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations
|
||||
* can be parallelized without requiring additional synchronization.
|
||||
*
|
||||
* <p>This is a terminal operation.
|
||||
*
|
||||
* Note There are many existing classes in the JDK whose signatures are
|
||||
* well-suited for use with method references as arguments to {@code collect()}.
|
||||
* For example, the following will accumulate strings into an {@code ArrayList}:
|
||||
* <pre>{@code
|
||||
* List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add,
|
||||
* ArrayList::addAll);
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The following will take a stream of strings and concatenates them into a
|
||||
* single string:
|
||||
* <pre>{@code
|
||||
* String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
|
||||
* StringBuilder::append)
|
||||
* .toString();
|
||||
* }</pre>
|
||||
*
|
||||
* @param <R> type of the result
|
||||
* @param <A> Type of the accumulator.
|
||||
* @param pupplier a function that creates a new result container. For a
|
||||
* parallel execution, this function may be called
|
||||
* multiple times and must return a fresh value each time.
|
||||
* @param accumulator An associative, non-interfering, stateless function for
|
||||
* incorporating an additional element into a result
|
||||
* @param combiner An associative, non-interfering, stateless
|
||||
* function for combining two values, which must be compatible with the
|
||||
* accumulator function
|
||||
* @return The result of the reduction
|
||||
*/
|
||||
public <A, R> R collect(final Supplier<R> pupplier, final BiConsumer<R, ? super O> accumulator, final BiConsumer<R, R> combiner) {
|
||||
makeTerminated();
|
||||
return stream().collect(pupplier, accumulator, combiner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a reduction on the elements of this stream, using the provided
|
||||
* identity value and an associative accumulation function, and returns
|
||||
* the reduced value. This is equivalent to:
|
||||
* <pre>{@code
|
||||
* T result = identity;
|
||||
* for (T element : this stream)
|
||||
* result = accumulator.apply(result, element)
|
||||
* return result;
|
||||
* }</pre>
|
||||
*
|
||||
* but is not constrained to execute sequentially.
|
||||
*
|
||||
* <p>The {@code identity} value must be an identity for the accumulator
|
||||
* function. This means that for all {@code t},
|
||||
* {@code accumulator.apply(identity, t)} is equal to {@code t}.
|
||||
* The {@code accumulator} function must be an associative function.
|
||||
*
|
||||
* <p>This is a terminal operation.
|
||||
*
|
||||
* Note Sum, min, max, average, and string concatenation are all special
|
||||
* cases of reduction. Summing a stream of numbers can be expressed as:
|
||||
*
|
||||
* <pre>{@code
|
||||
* Integer sum = integers.reduce(0, (a, b) -> a+b);
|
||||
* }</pre>
|
||||
*
|
||||
* or:
|
||||
*
|
||||
* <pre>{@code
|
||||
* Integer sum = integers.reduce(0, Integer::sum);
|
||||
* }</pre>
|
||||
*
|
||||
* <p>While this may seem a more roundabout way to perform an aggregation
|
||||
* compared to simply mutating a running total in a loop, reduction
|
||||
* operations parallelize more gracefully, without needing additional
|
||||
* synchronization and with greatly reduced risk of data races.
|
||||
*
|
||||
* @param identity the identity value for the accumulating function
|
||||
* @param accumulator an associative, non-interfering, stateless
|
||||
* function for combining two values
|
||||
* @return the result of the reduction
|
||||
*/
|
||||
public O reduce(final O identity, final BinaryOperator<O> accumulator) {
|
||||
makeTerminated();
|
||||
return stream().reduce(identity, accumulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream consisting of the results of applying the given
|
||||
* function to the elements of this stream.
|
||||
*
|
||||
* <p>This is an intermediate operation.
|
||||
*
|
||||
* @param <R> The element type of the new stream
|
||||
* @param mapper A non-interfering, stateless function to apply to each element
|
||||
* @return the new stream
|
||||
*/
|
||||
public <R> FailableStream<R> map(final FailableFunction<O, R, ?> mapper) {
|
||||
assertNotTerminated();
|
||||
return new FailableStream<>(stream.map(Functions.asFunction(mapper)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the FailableStream into an equivalent stream.
|
||||
* @return A stream, which will return the same elements, which this FailableStream would return.
|
||||
*/
|
||||
public Stream<O> stream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether all elements of this stream match the provided predicate.
|
||||
* May not evaluate the predicate on all elements if not necessary for
|
||||
* determining the result. If the stream is empty then {@code true} is
|
||||
* returned and the predicate is not evaluated.
|
||||
*
|
||||
* <p>This is a short-circuiting terminal operation.
|
||||
*
|
||||
* Note
|
||||
* This method evaluates the <em>universal quantification</em> of the
|
||||
* predicate over the elements of the stream (for all x P(x)). If the
|
||||
* stream is empty, the quantification is said to be <em>vacuously
|
||||
* satisfied</em> and is always {@code true} (regardless of P(x)).
|
||||
*
|
||||
* @param predicate A non-interfering, stateless predicate to apply to
|
||||
* elements of this stream
|
||||
* @return {@code true} If either all elements of the stream match the
|
||||
* provided predicate or the stream is empty, otherwise {@code false}.
|
||||
*/
|
||||
public boolean allMatch(final FailablePredicate<O, ?> predicate) {
|
||||
assertNotTerminated();
|
||||
return stream().allMatch(Functions.asPredicate(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether any elements of this stream match the provided
|
||||
* predicate. May not evaluate the predicate on all elements if not
|
||||
* necessary for determining the result. If the stream is empty then
|
||||
* {@code false} is returned and the predicate is not evaluated.
|
||||
*
|
||||
* <p>This is a short-circuiting terminal operation.
|
||||
*
|
||||
* Note
|
||||
* This method evaluates the <em>existential quantification</em> of the
|
||||
* predicate over the elements of the stream (for some x P(x)).
|
||||
*
|
||||
* @param predicate A non-interfering, stateless predicate to apply to
|
||||
* elements of this stream
|
||||
* @return {@code true} if any elements of the stream match the provided
|
||||
* predicate, otherwise {@code false}
|
||||
*/
|
||||
public boolean anyMatch(final FailablePredicate<O, ?> predicate) {
|
||||
assertNotTerminated();
|
||||
return stream().anyMatch(Functions.asPredicate(predicate));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link Stream stream} into a {@link FailableStream}.
|
||||
* This is basically a simplified, reduced version of the {@link Stream}
|
||||
* class, with the same underlying element stream, except that failable
|
||||
* objects, like {@link FailablePredicate}, {@link FailableFunction}, or
|
||||
* {@link FailableConsumer} may be applied, instead of
|
||||
* {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
|
||||
* to rewrite a code snippet like this:
|
||||
* <pre>
|
||||
* final List<O> list;
|
||||
* final Method m;
|
||||
* final Function<O,String> mapper = (o) -> {
|
||||
* try {
|
||||
* return (String) m.invoke(o);
|
||||
* } catch (Throwable t) {
|
||||
* throw Functions.rethrow(t);
|
||||
* }
|
||||
* };
|
||||
* final List<String> strList = list.stream()
|
||||
* .map(mapper).collect(Collectors.toList());
|
||||
* </pre>
|
||||
* as follows:
|
||||
* <pre>
|
||||
* final List<O> list;
|
||||
* final Method m;
|
||||
* final List<String> strList = Functions.stream(list.stream())
|
||||
* .map((o) -> (String) m.invoke(o)).collect(Collectors.toList());
|
||||
* </pre>
|
||||
* While the second version may not be <em>quite</em> as
|
||||
* efficient (because it depends on the creation of additional,
|
||||
* intermediate objects, of type FailableStream), it is much more
|
||||
* concise, and readable, and meets the spirit of Lambdas better
|
||||
* than the first version.
|
||||
* @param <O> The streams element type.
|
||||
* @param stream The stream, which is being converted.
|
||||
* @return The {@link FailableStream}, which has been created by
|
||||
* converting the stream.
|
||||
*/
|
||||
public static <O> FailableStream<O> stream(final Stream<O> stream) {
|
||||
return new FailableStream<>(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link Collection} into a {@link FailableStream}.
|
||||
* This is basically a simplified, reduced version of the {@link Stream}
|
||||
* class, with the same underlying element stream, except that failable
|
||||
* objects, like {@link FailablePredicate}, {@link FailableFunction}, or
|
||||
* {@link FailableConsumer} may be applied, instead of
|
||||
* {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
|
||||
* to rewrite a code snippet like this:
|
||||
* <pre>
|
||||
* final List<O> list;
|
||||
* final Method m;
|
||||
* final Function<O,String> mapper = (o) -> {
|
||||
* try {
|
||||
* return (String) m.invoke(o);
|
||||
* } catch (Throwable t) {
|
||||
* throw Functions.rethrow(t);
|
||||
* }
|
||||
* };
|
||||
* final List<String> strList = list.stream()
|
||||
* .map(mapper).collect(Collectors.toList());
|
||||
* </pre>
|
||||
* as follows:
|
||||
* <pre>
|
||||
* final List<O> list;
|
||||
* final Method m;
|
||||
* final List<String> strList = Functions.stream(list.stream())
|
||||
* .map((o) -> (String) m.invoke(o)).collect(Collectors.toList());
|
||||
* </pre>
|
||||
* While the second version may not be <em>quite</em> as
|
||||
* efficient (because it depends on the creation of additional,
|
||||
* intermediate objects, of type FailableStream), it is much more
|
||||
* concise, and readable, and meets the spirit of Lambdas better
|
||||
* than the first version.
|
||||
* @param <O> The streams element type.
|
||||
* @param stream The stream, which is being converted.
|
||||
* @return The {@link FailableStream}, which has been created by
|
||||
* converting the stream.
|
||||
*/
|
||||
public static <O> FailableStream<O> stream(final Collection<O> stream) {
|
||||
return stream(stream.stream());
|
||||
}
|
||||
|
||||
/**
|
||||
* A Collector type for arrays.
|
||||
*
|
||||
* @param <O> The array type.
|
||||
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams.ArrayCollector}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static class ArrayCollector<O> implements Collector<O, List<O>, O[]> {
|
||||
private static final Set<Characteristics> characteristics = Collections.emptySet();
|
||||
private final Class<O> elementType;
|
||||
|
||||
/**
|
||||
* Constructs a new instance for the given element type.
|
||||
*
|
||||
* @param elementType The element type.
|
||||
*/
|
||||
public ArrayCollector(final Class<O> elementType) {
|
||||
this.elementType = elementType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<List<O>> supplier() {
|
||||
return ArrayList::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiConsumer<List<O>, O> accumulator() {
|
||||
return List::add;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryOperator<List<O>> combiner() {
|
||||
return (left, right) -> {
|
||||
left.addAll(right);
|
||||
return left;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<List<O>, O[]> finisher() {
|
||||
return list -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
final O[] array = (O[]) Array.newInstance(elementType, list.size());
|
||||
return list.toArray(array);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Characteristics> characteristics() {
|
||||
return characteristics;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Collector} that accumulates the input elements into a
|
||||
* new array.
|
||||
*
|
||||
* @param pElementType Type of an element in the array.
|
||||
* @param <O> the type of the input elements
|
||||
* @return a {@code Collector} which collects all the input elements into an
|
||||
* array, in encounter order
|
||||
*/
|
||||
public static <O extends Object> Collector<O, ?, O[]> toArray(final Class<O> pElementType) {
|
||||
return new ArrayCollector<>(pElementType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,805 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.apache.commons.lang3.text.translate.AggregateTranslator;
|
||||
import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
|
||||
import org.apache.commons.lang3.text.translate.EntityArrays;
|
||||
import org.apache.commons.lang3.text.translate.JavaUnicodeEscaper;
|
||||
import org.apache.commons.lang3.text.translate.LookupTranslator;
|
||||
import org.apache.commons.lang3.text.translate.NumericEntityEscaper;
|
||||
import org.apache.commons.lang3.text.translate.NumericEntityUnescaper;
|
||||
import org.apache.commons.lang3.text.translate.OctalUnescaper;
|
||||
import org.apache.commons.lang3.text.translate.UnicodeUnescaper;
|
||||
import org.apache.commons.lang3.text.translate.UnicodeUnpairedSurrogateRemover;
|
||||
|
||||
/**
|
||||
* <p>Escapes and unescapes {@code String}s for
|
||||
* Java, Java Script, HTML and XML.</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
* @since 2.0
|
||||
* @deprecated as of 3.6, use commons-text
|
||||
* <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/StringEscapeUtils.html">
|
||||
* StringEscapeUtils</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class StringEscapeUtils {
|
||||
|
||||
/* ESCAPE TRANSLATORS */
|
||||
|
||||
/**
|
||||
* Translator object for escaping Java.
|
||||
*
|
||||
* While {@link #escapeJava(String)} is the expected method of use, this
|
||||
* object allows the Java escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_JAVA =
|
||||
new LookupTranslator(
|
||||
new String[][] {
|
||||
{"\"", "\\\""},
|
||||
{"\\", "\\\\"},
|
||||
}).with(
|
||||
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE())
|
||||
).with(
|
||||
JavaUnicodeEscaper.outsideOf(32, 0x7f)
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping EcmaScript/JavaScript.
|
||||
*
|
||||
* While {@link #escapeEcmaScript(String)} is the expected method of use, this
|
||||
* object allows the EcmaScript escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_ECMASCRIPT =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(
|
||||
new String[][] {
|
||||
{"'", "\\'"},
|
||||
{"\"", "\\\""},
|
||||
{"\\", "\\\\"},
|
||||
{"/", "\\/"}
|
||||
}),
|
||||
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()),
|
||||
JavaUnicodeEscaper.outsideOf(32, 0x7f)
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping Json.
|
||||
*
|
||||
* While {@link #escapeJson(String)} is the expected method of use, this
|
||||
* object allows the Json escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_JSON =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(
|
||||
new String[][] {
|
||||
{"\"", "\\\""},
|
||||
{"\\", "\\\\"},
|
||||
{"/", "\\/"}
|
||||
}),
|
||||
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()),
|
||||
JavaUnicodeEscaper.outsideOf(32, 0x7f)
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping XML.
|
||||
*
|
||||
* While {@link #escapeXml(String)} is the expected method of use, this
|
||||
* object allows the XML escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
* @deprecated use {@link #ESCAPE_XML10} or {@link #ESCAPE_XML11} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final CharSequenceTranslator ESCAPE_XML =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||
new LookupTranslator(EntityArrays.APOS_ESCAPE())
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping XML 1.0.
|
||||
*
|
||||
* While {@link #escapeXml10(String)} is the expected method of use, this
|
||||
* object allows the XML escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_XML10 =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||
new LookupTranslator(EntityArrays.APOS_ESCAPE()),
|
||||
new LookupTranslator(
|
||||
new String[][] {
|
||||
{ "\u0000", StringUtils.EMPTY },
|
||||
{ "\u0001", StringUtils.EMPTY },
|
||||
{ "\u0002", StringUtils.EMPTY },
|
||||
{ "\u0003", StringUtils.EMPTY },
|
||||
{ "\u0004", StringUtils.EMPTY },
|
||||
{ "\u0005", StringUtils.EMPTY },
|
||||
{ "\u0006", StringUtils.EMPTY },
|
||||
{ "\u0007", StringUtils.EMPTY },
|
||||
{ "\u0008", StringUtils.EMPTY },
|
||||
{ "\u000b", StringUtils.EMPTY },
|
||||
{ "\u000c", StringUtils.EMPTY },
|
||||
{ "\u000e", StringUtils.EMPTY },
|
||||
{ "\u000f", StringUtils.EMPTY },
|
||||
{ "\u0010", StringUtils.EMPTY },
|
||||
{ "\u0011", StringUtils.EMPTY },
|
||||
{ "\u0012", StringUtils.EMPTY },
|
||||
{ "\u0013", StringUtils.EMPTY },
|
||||
{ "\u0014", StringUtils.EMPTY },
|
||||
{ "\u0015", StringUtils.EMPTY },
|
||||
{ "\u0016", StringUtils.EMPTY },
|
||||
{ "\u0017", StringUtils.EMPTY },
|
||||
{ "\u0018", StringUtils.EMPTY },
|
||||
{ "\u0019", StringUtils.EMPTY },
|
||||
{ "\u001a", StringUtils.EMPTY },
|
||||
{ "\u001b", StringUtils.EMPTY },
|
||||
{ "\u001c", StringUtils.EMPTY },
|
||||
{ "\u001d", StringUtils.EMPTY },
|
||||
{ "\u001e", StringUtils.EMPTY },
|
||||
{ "\u001f", StringUtils.EMPTY },
|
||||
{ "\ufffe", StringUtils.EMPTY },
|
||||
{ "\uffff", StringUtils.EMPTY }
|
||||
}),
|
||||
NumericEntityEscaper.between(0x7f, 0x84),
|
||||
NumericEntityEscaper.between(0x86, 0x9f),
|
||||
new UnicodeUnpairedSurrogateRemover()
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping XML 1.1.
|
||||
*
|
||||
* While {@link #escapeXml11(String)} is the expected method of use, this
|
||||
* object allows the XML escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_XML11 =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||
new LookupTranslator(EntityArrays.APOS_ESCAPE()),
|
||||
new LookupTranslator(
|
||||
new String[][] {
|
||||
{ "\u0000", StringUtils.EMPTY },
|
||||
{ "\u000b", "" },
|
||||
{ "\u000c", "" },
|
||||
{ "\ufffe", StringUtils.EMPTY },
|
||||
{ "\uffff", StringUtils.EMPTY }
|
||||
}),
|
||||
NumericEntityEscaper.between(0x1, 0x8),
|
||||
NumericEntityEscaper.between(0xe, 0x1f),
|
||||
NumericEntityEscaper.between(0x7f, 0x84),
|
||||
NumericEntityEscaper.between(0x86, 0x9f),
|
||||
new UnicodeUnpairedSurrogateRemover()
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping HTML version 3.0.
|
||||
*
|
||||
* While {@link #escapeHtml3(String)} is the expected method of use, this
|
||||
* object allows the HTML escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_HTML3 =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||
new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE())
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping HTML version 4.0.
|
||||
*
|
||||
* While {@link #escapeHtml4(String)} is the expected method of use, this
|
||||
* object allows the HTML escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_HTML4 =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||
new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()),
|
||||
new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE())
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for escaping individual Comma Separated Values.
|
||||
*
|
||||
* While {@link #escapeCsv(String)} is the expected method of use, this
|
||||
* object allows the CSV escaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper();
|
||||
|
||||
// TODO: Create a parent class - 'SinglePassTranslator' ?
|
||||
// It would handle the index checking + length returning,
|
||||
// and could also have an optimization check method.
|
||||
static class CsvEscaper extends CharSequenceTranslator {
|
||||
|
||||
private static final char CSV_DELIMITER = ',';
|
||||
private static final char CSV_QUOTE = '"';
|
||||
private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
|
||||
private static final char[] CSV_SEARCH_CHARS = { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF };
|
||||
|
||||
@Override
|
||||
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
|
||||
|
||||
if (index != 0) {
|
||||
throw new IllegalStateException("CsvEscaper should never reach the [1] index");
|
||||
}
|
||||
|
||||
if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) {
|
||||
out.write(input.toString());
|
||||
} else {
|
||||
out.write(CSV_QUOTE);
|
||||
out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR));
|
||||
out.write(CSV_QUOTE);
|
||||
}
|
||||
return Character.codePointCount(input, 0, input.length());
|
||||
}
|
||||
}
|
||||
|
||||
/* UNESCAPE TRANSLATORS */
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped Java.
|
||||
*
|
||||
* While {@link #unescapeJava(String)} is the expected method of use, this
|
||||
* object allows the Java unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
// TODO: throw "illegal character: \92" as an Exception if a \ on the end of the Java (as per the compiler)?
|
||||
public static final CharSequenceTranslator UNESCAPE_JAVA =
|
||||
new AggregateTranslator(
|
||||
new OctalUnescaper(), // .between('\1', '\377'),
|
||||
new UnicodeUnescaper(),
|
||||
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()),
|
||||
new LookupTranslator(
|
||||
new String[][] {
|
||||
{"\\\\", "\\"},
|
||||
{"\\\"", "\""},
|
||||
{"\\'", "'"},
|
||||
{"\\", ""}
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped EcmaScript.
|
||||
*
|
||||
* While {@link #unescapeEcmaScript(String)} is the expected method of use, this
|
||||
* object allows the EcmaScript unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA;
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped Json.
|
||||
*
|
||||
* While {@link #unescapeJson(String)} is the expected method of use, this
|
||||
* object allows the Json unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA;
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped HTML 3.0.
|
||||
*
|
||||
* While {@link #unescapeHtml3(String)} is the expected method of use, this
|
||||
* object allows the HTML unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator UNESCAPE_HTML3 =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
|
||||
new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
|
||||
new NumericEntityUnescaper()
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped HTML 4.0.
|
||||
*
|
||||
* While {@link #unescapeHtml4(String)} is the expected method of use, this
|
||||
* object allows the HTML unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator UNESCAPE_HTML4 =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
|
||||
new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
|
||||
new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()),
|
||||
new NumericEntityUnescaper()
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped XML.
|
||||
*
|
||||
* While {@link #unescapeXml(String)} is the expected method of use, this
|
||||
* object allows the XML unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator UNESCAPE_XML =
|
||||
new AggregateTranslator(
|
||||
new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
|
||||
new LookupTranslator(EntityArrays.APOS_UNESCAPE()),
|
||||
new NumericEntityUnescaper()
|
||||
);
|
||||
|
||||
/**
|
||||
* Translator object for unescaping escaped Comma Separated Value entries.
|
||||
*
|
||||
* While {@link #unescapeCsv(String)} is the expected method of use, this
|
||||
* object allows the CSV unescaping functionality to be used
|
||||
* as the foundation for a custom translator.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper();
|
||||
|
||||
static class CsvUnescaper extends CharSequenceTranslator {
|
||||
|
||||
private static final char CSV_DELIMITER = ',';
|
||||
private static final char CSV_QUOTE = '"';
|
||||
private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
|
||||
private static final char[] CSV_SEARCH_CHARS = {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF};
|
||||
|
||||
@Override
|
||||
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
|
||||
|
||||
if (index != 0) {
|
||||
throw new IllegalStateException("CsvUnescaper should never reach the [1] index");
|
||||
}
|
||||
|
||||
if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) {
|
||||
out.write(input.toString());
|
||||
return Character.codePointCount(input, 0, input.length());
|
||||
}
|
||||
|
||||
// strip quotes
|
||||
final String quoteless = input.subSequence(1, input.length() - 1).toString();
|
||||
|
||||
if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) {
|
||||
// deal with escaped quotes; ie) ""
|
||||
out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR));
|
||||
} else {
|
||||
out.write(input.toString());
|
||||
}
|
||||
return Character.codePointCount(input, 0, input.length());
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
|
||||
/**
|
||||
* <p>{@code StringEscapeUtils} instances should NOT be constructed in
|
||||
* standard programming.</p>
|
||||
*
|
||||
* <p>Instead, the class should be used as:</p>
|
||||
* <pre>StringEscapeUtils.escapeJava("foo");</pre>
|
||||
*
|
||||
* <p>This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.</p>
|
||||
*/
|
||||
public StringEscapeUtils() {
|
||||
}
|
||||
|
||||
// Java and JavaScript
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using Java String rules.</p>
|
||||
*
|
||||
* <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
|
||||
*
|
||||
* <p>So a tab becomes the characters {@code '\\'} and
|
||||
* {@code 't'}.</p>
|
||||
*
|
||||
* <p>The only difference between Java strings and JavaScript strings
|
||||
* is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
|
||||
*
|
||||
* <p>Example:</p>
|
||||
* <pre>
|
||||
* input string: He didn't say, "Stop!"
|
||||
* output string: He didn't say, \"Stop!\"
|
||||
* </pre>
|
||||
*
|
||||
* @param input String to escape values in, may be null
|
||||
* @return String with escaped values, {@code null} if null string input
|
||||
*/
|
||||
public static final String escapeJava(final String input) {
|
||||
return ESCAPE_JAVA.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using EcmaScript String rules.</p>
|
||||
* <p>Escapes any values it finds into their EcmaScript String form.
|
||||
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
|
||||
*
|
||||
* <p>So a tab becomes the characters {@code '\\'} and
|
||||
* {@code 't'}.</p>
|
||||
*
|
||||
* <p>The only difference between Java strings and EcmaScript strings
|
||||
* is that in EcmaScript, a single quote and forward-slash (/) are escaped.</p>
|
||||
*
|
||||
* <p>Note that EcmaScript is best known by the JavaScript and ActionScript dialects. </p>
|
||||
*
|
||||
* <p>Example:</p>
|
||||
* <pre>
|
||||
* input string: He didn't say, "Stop!"
|
||||
* output string: He didn\'t say, \"Stop!\"
|
||||
* </pre>
|
||||
*
|
||||
* @param input String to escape values in, may be null
|
||||
* @return String with escaped values, {@code null} if null string input
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String escapeEcmaScript(final String input) {
|
||||
return ESCAPE_ECMASCRIPT.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using Json String rules.</p>
|
||||
* <p>Escapes any values it finds into their Json String form.
|
||||
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
|
||||
*
|
||||
* <p>So a tab becomes the characters {@code '\\'} and
|
||||
* {@code 't'}.</p>
|
||||
*
|
||||
* <p>The only difference between Java strings and Json strings
|
||||
* is that in Json, forward-slash (/) is escaped.</p>
|
||||
*
|
||||
* <p>See http://www.ietf.org/rfc/rfc4627.txt for further details. </p>
|
||||
*
|
||||
* <p>Example:</p>
|
||||
* <pre>
|
||||
* input string: He didn't say, "Stop!"
|
||||
* output string: He didn't say, \"Stop!\"
|
||||
* </pre>
|
||||
*
|
||||
* @param input String to escape values in, may be null
|
||||
* @return String with escaped values, {@code null} if null string input
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public static final String escapeJson(final String input) {
|
||||
return ESCAPE_JSON.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unescapes any Java literals found in the {@code String}.
|
||||
* For example, it will turn a sequence of {@code '\'} and
|
||||
* {@code 'n'} into a newline character, unless the {@code '\'}
|
||||
* is preceded by another {@code '\'}.</p>
|
||||
*
|
||||
* @param input the {@code String} to unescape, may be null
|
||||
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||
*/
|
||||
public static final String unescapeJava(final String input) {
|
||||
return UNESCAPE_JAVA.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unescapes any EcmaScript literals found in the {@code String}.</p>
|
||||
*
|
||||
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
|
||||
* into a newline character, unless the {@code '\'} is preceded by another
|
||||
* {@code '\'}.</p>
|
||||
*
|
||||
* @see #unescapeJava(String)
|
||||
* @param input the {@code String} to unescape, may be null
|
||||
* @return A new unescaped {@code String}, {@code null} if null string input
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String unescapeEcmaScript(final String input) {
|
||||
return UNESCAPE_ECMASCRIPT.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unescapes any Json literals found in the {@code String}.</p>
|
||||
*
|
||||
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
|
||||
* into a newline character, unless the {@code '\'} is preceded by another
|
||||
* {@code '\'}.</p>
|
||||
*
|
||||
* @see #unescapeJava(String)
|
||||
* @param input the {@code String} to unescape, may be null
|
||||
* @return A new unescaped {@code String}, {@code null} if null string input
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public static final String unescapeJson(final String input) {
|
||||
return UNESCAPE_JSON.translate(input);
|
||||
}
|
||||
|
||||
// HTML and XML
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using HTML entities.</p>
|
||||
*
|
||||
* <p>
|
||||
* For example:
|
||||
* </p>
|
||||
* <p>{@code "bread" & "butter"}</p>
|
||||
* becomes:
|
||||
* <p>
|
||||
* {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
|
||||
* </p>
|
||||
*
|
||||
* <p>Supports all known HTML 4.0 entities, including funky accents.
|
||||
* Note that the commonly used apostrophe escape character (&apos;)
|
||||
* is not a legal entity and so is not supported). </p>
|
||||
*
|
||||
* @param input the {@code String} to escape, may be null
|
||||
* @return a new escaped {@code String}, {@code null} if null string input
|
||||
*
|
||||
* @see <a href="http://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO Entities</a>
|
||||
* @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character Entities for ISO Latin-1</a>
|
||||
* @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0 Character entity references</a>
|
||||
* @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01 Character References</a>
|
||||
* @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML 4.01 Code positions</a>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String escapeHtml4(final String input) {
|
||||
return ESCAPE_HTML4.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using HTML entities.</p>
|
||||
* <p>Supports only the HTML 3.0 entities. </p>
|
||||
*
|
||||
* @param input the {@code String} to escape, may be null
|
||||
* @return a new escaped {@code String}, {@code null} if null string input
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String escapeHtml3(final String input) {
|
||||
return ESCAPE_HTML3.translate(input);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Unescapes a string containing entity escapes to a string
|
||||
* containing the actual Unicode characters corresponding to the
|
||||
* escapes. Supports HTML 4.0 entities.</p>
|
||||
*
|
||||
* <p>For example, the string {@code "<Français>"}
|
||||
* will become {@code "<Français>"}</p>
|
||||
*
|
||||
* <p>If an entity is unrecognized, it is left alone, and inserted
|
||||
* verbatim into the result string. e.g. {@code ">&zzzz;x"} will
|
||||
* become {@code ">&zzzz;x"}.</p>
|
||||
*
|
||||
* @param input the {@code String} to unescape, may be null
|
||||
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String unescapeHtml4(final String input) {
|
||||
return UNESCAPE_HTML4.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unescapes a string containing entity escapes to a string
|
||||
* containing the actual Unicode characters corresponding to the
|
||||
* escapes. Supports only HTML 3.0 entities.</p>
|
||||
*
|
||||
* @param input the {@code String} to unescape, may be null
|
||||
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String unescapeHtml3(final String input) {
|
||||
return UNESCAPE_HTML3.translate(input);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using XML entities.</p>
|
||||
*
|
||||
* <p>For example: {@code "bread" & "butter"} =>
|
||||
* {@code "bread" & "butter"}.
|
||||
* </p>
|
||||
*
|
||||
* <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
|
||||
* Does not support DTDs or external entities.</p>
|
||||
*
|
||||
* <p>Note that Unicode characters greater than 0x7f are as of 3.0, no longer
|
||||
* escaped. If you still wish this functionality, you can achieve it
|
||||
* via the following:
|
||||
* {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );}</p>
|
||||
*
|
||||
* @param input the {@code String} to escape, may be null
|
||||
* @return a new escaped {@code String}, {@code null} if null string input
|
||||
* @see #unescapeXml(java.lang.String)
|
||||
* @deprecated use {@link #escapeXml10(java.lang.String)} or {@link #escapeXml11(java.lang.String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String escapeXml(final String input) {
|
||||
return ESCAPE_XML.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using XML entities.</p>
|
||||
*
|
||||
* <p>For example: {@code "bread" & "butter"} =>
|
||||
* {@code "bread" & "butter"}.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that XML 1.0 is a text-only format: it cannot represent control
|
||||
* characters or unpaired Unicode surrogate codepoints, even after escaping.
|
||||
* {@code escapeXml10} will remove characters that do not fit in the
|
||||
* following ranges:</p>
|
||||
*
|
||||
* <p>{@code #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
|
||||
*
|
||||
* <p>Though not strictly necessary, {@code escapeXml10} will escape
|
||||
* characters in the following ranges:</p>
|
||||
*
|
||||
* <p>{@code [#x7F-#x84] | [#x86-#x9F]}</p>
|
||||
*
|
||||
* <p>The returned string can be inserted into a valid XML 1.0 or XML 1.1
|
||||
* document. If you want to allow more non-text characters in an XML 1.1
|
||||
* document, use {@link #escapeXml11(String)}.</p>
|
||||
*
|
||||
* @param input the {@code String} to escape, may be null
|
||||
* @return a new escaped {@code String}, {@code null} if null string input
|
||||
* @see #unescapeXml(java.lang.String)
|
||||
* @since 3.3
|
||||
*/
|
||||
public static String escapeXml10(final String input) {
|
||||
return ESCAPE_XML10.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Escapes the characters in a {@code String} using XML entities.</p>
|
||||
*
|
||||
* <p>For example: {@code "bread" & "butter"} =>
|
||||
* {@code "bread" & "butter"}.
|
||||
* </p>
|
||||
*
|
||||
* <p>XML 1.1 can represent certain control characters, but it cannot represent
|
||||
* the null byte or unpaired Unicode surrogate codepoints, even after escaping.
|
||||
* {@code escapeXml11} will remove characters that do not fit in the following
|
||||
* ranges:</p>
|
||||
*
|
||||
* <p>{@code [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
|
||||
*
|
||||
* <p>{@code escapeXml11} will escape characters in the following ranges:</p>
|
||||
*
|
||||
* <p>{@code [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]}</p>
|
||||
*
|
||||
* <p>The returned string can be inserted into a valid XML 1.1 document. Do not
|
||||
* use it for XML 1.0 documents.</p>
|
||||
*
|
||||
* @param input the {@code String} to escape, may be null
|
||||
* @return a new escaped {@code String}, {@code null} if null string input
|
||||
* @see #unescapeXml(java.lang.String)
|
||||
* @since 3.3
|
||||
*/
|
||||
public static String escapeXml11(final String input) {
|
||||
return ESCAPE_XML11.translate(input);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Unescapes a string containing XML entity escapes to a string
|
||||
* containing the actual Unicode characters corresponding to the
|
||||
* escapes.</p>
|
||||
*
|
||||
* <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
|
||||
* Does not support DTDs or external entities.</p>
|
||||
*
|
||||
* <p>Note that numerical \\u Unicode codes are unescaped to their respective
|
||||
* Unicode characters. This may change in future releases. </p>
|
||||
*
|
||||
* @param input the {@code String} to unescape, may be null
|
||||
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||
* @see #escapeXml(String)
|
||||
* @see #escapeXml10(String)
|
||||
* @see #escapeXml11(String)
|
||||
*/
|
||||
public static final String unescapeXml(final String input) {
|
||||
return UNESCAPE_XML.translate(input);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Returns a {@code String} value for a CSV column enclosed in double quotes,
|
||||
* if required.</p>
|
||||
*
|
||||
* <p>If the value contains a comma, newline or double quote, then the
|
||||
* String value is returned enclosed in double quotes.</p>
|
||||
*
|
||||
* <p>Any double quote characters in the value are escaped with another double quote.</p>
|
||||
*
|
||||
* <p>If the value does not contain a comma, newline or double quote, then the
|
||||
* String value is returned unchanged.</p>
|
||||
*
|
||||
* see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
|
||||
* <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||||
*
|
||||
* @param input the input CSV column String, may be null
|
||||
* @return the input String, enclosed in double quotes if the value contains a comma,
|
||||
* newline or double quote, {@code null} if null string input
|
||||
* @since 2.4
|
||||
*/
|
||||
public static final String escapeCsv(final String input) {
|
||||
return ESCAPE_CSV.translate(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a {@code String} value for an unescaped CSV column. </p>
|
||||
*
|
||||
* <p>If the value is enclosed in double quotes, and contains a comma, newline
|
||||
* or double quote, then quotes are removed.
|
||||
* </p>
|
||||
*
|
||||
* <p>Any double quote escaped characters (a pair of double quotes) are unescaped
|
||||
* to just one double quote. </p>
|
||||
*
|
||||
* <p>If the value is not enclosed in double quotes, or is and does not contain a
|
||||
* comma, newline or double quote, then the String value is returned unchanged.</p>
|
||||
*
|
||||
* see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
|
||||
* <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||||
*
|
||||
* @param input the input CSV column String, may be null
|
||||
* @return the input String, with enclosing double quotes removed and embedded double
|
||||
* quotes unescaped, {@code null} if null string input
|
||||
* @since 2.4
|
||||
*/
|
||||
public static final String unescapeCsv(final String input) {
|
||||
return UNESCAPE_CSV.translate(input);
|
||||
}
|
||||
|
||||
}
|
9650
before/src/main/java/org/apache/commons/lang3/StringUtils.java
Normal file
9650
before/src/main/java/org/apache/commons/lang3/StringUtils.java
Normal file
File diff suppressed because it is too large
Load diff
1945
before/src/main/java/org/apache/commons/lang3/SystemUtils.java
Normal file
1945
before/src/main/java/org/apache/commons/lang3/SystemUtils.java
Normal file
File diff suppressed because it is too large
Load diff
469
before/src/main/java/org/apache/commons/lang3/ThreadUtils.java
Normal file
469
before/src/main/java/org/apache/commons/lang3/ThreadUtils.java
Normal file
|
@ -0,0 +1,469 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.time.DurationUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}.
|
||||
* </p>
|
||||
* <p>
|
||||
* #ThreadSafe#
|
||||
* </p>
|
||||
*
|
||||
* @see java.lang.Thread
|
||||
* @see java.lang.ThreadGroup
|
||||
* @since 3.5
|
||||
*/
|
||||
public class ThreadUtils {
|
||||
|
||||
/**
|
||||
* A predicate implementation which always returns true.
|
||||
*/
|
||||
private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate {
|
||||
|
||||
private AlwaysTruePredicate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final Thread thread) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final ThreadGroup threadGroup) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate implementation which matches a thread or threadgroup name.
|
||||
*/
|
||||
public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Predicate constructor
|
||||
*
|
||||
* @param name thread or threadgroup name
|
||||
* @throws IllegalArgumentException if the name is {@code null}
|
||||
*/
|
||||
public NamePredicate(final String name) {
|
||||
Validate.notNull(name, "name");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final Thread thread) {
|
||||
return thread != null && thread.getName().equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final ThreadGroup threadGroup) {
|
||||
return threadGroup != null && threadGroup.getName().equals(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate for selecting threadgroups.
|
||||
*/
|
||||
// When breaking BC, replace this with Predicate<ThreadGroup>
|
||||
@FunctionalInterface
|
||||
public interface ThreadGroupPredicate {
|
||||
|
||||
/**
|
||||
* Evaluates this predicate on the given threadgroup.
|
||||
* @param threadGroup the threadgroup
|
||||
* @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
|
||||
*/
|
||||
boolean test(ThreadGroup threadGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate implementation which matches a thread id.
|
||||
*/
|
||||
public static class ThreadIdPredicate implements ThreadPredicate {
|
||||
|
||||
private final long threadId;
|
||||
|
||||
/**
|
||||
* Predicate constructor
|
||||
*
|
||||
* @param threadId the threadId to match
|
||||
* @throws IllegalArgumentException if the threadId is zero or negative
|
||||
*/
|
||||
public ThreadIdPredicate(final long threadId) {
|
||||
if (threadId <= 0) {
|
||||
throw new IllegalArgumentException("The thread id must be greater than zero");
|
||||
}
|
||||
this.threadId = threadId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final Thread thread) {
|
||||
return thread != null && thread.getId() == threadId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate for selecting threads.
|
||||
*/
|
||||
// When breaking BC, replace this with Predicate<Thread>
|
||||
@FunctionalInterface
|
||||
public interface ThreadPredicate {
|
||||
|
||||
/**
|
||||
* Evaluates this predicate on the given thread.
|
||||
* @param thread the thread
|
||||
* @return {@code true} if the thread matches the predicate, otherwise {@code false}
|
||||
*/
|
||||
boolean test(Thread thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate which always returns true.
|
||||
*/
|
||||
public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
|
||||
|
||||
/**
|
||||
* Finds the active thread with the specified id.
|
||||
*
|
||||
* @param threadId The thread id
|
||||
* @return The thread with the specified id or {@code null} if no such thread exists
|
||||
* @throws IllegalArgumentException if the specified id is zero or negative
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Thread findThreadById(final long threadId) {
|
||||
final Collection<Thread> result = findThreads(new ThreadIdPredicate(threadId));
|
||||
return result.isEmpty() ? null : result.iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the active thread with the specified id if it belongs to a thread group with the specified group name.
|
||||
*
|
||||
* @param threadId The thread id
|
||||
* @param threadGroupName The thread group name
|
||||
* @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id.
|
||||
* {@code null} is returned if no such thread exists
|
||||
* @throws IllegalArgumentException if the specified id is zero or negative or the group name is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Thread findThreadById(final long threadId, final String threadGroupName) {
|
||||
Validate.notNull(threadGroupName, "threadGroupName");
|
||||
final Thread thread = findThreadById(threadId);
|
||||
if (thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) {
|
||||
return thread;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the active thread with the specified id if it belongs to the specified thread group.
|
||||
*
|
||||
* @param threadId The thread id
|
||||
* @param threadGroup The thread group
|
||||
* @return The thread which belongs to a specified thread group and the thread's id match the specified id.
|
||||
* {@code null} is returned if no such thread exists
|
||||
* @throws IllegalArgumentException if the specified id is zero or negative or the group is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) {
|
||||
Validate.notNull(threadGroup, "threadGroup");
|
||||
final Thread thread = findThreadById(threadId);
|
||||
if (thread != null && threadGroup.equals(thread.getThreadGroup())) {
|
||||
return thread;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
|
||||
*
|
||||
* @param group the thread group
|
||||
* @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
|
||||
* @param predicate the predicate
|
||||
* @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
|
||||
* @throws IllegalArgumentException if the given group or predicate is null
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate) {
|
||||
Validate.notNull(group, "group");
|
||||
Validate.notNull(predicate, "predicate");
|
||||
|
||||
int count = group.activeGroupCount();
|
||||
ThreadGroup[] threadGroups;
|
||||
do {
|
||||
threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size
|
||||
count = group.enumerate(threadGroups, recurse);
|
||||
//return value of enumerate() must be strictly less than the array size according to javadoc
|
||||
} while (count >= threadGroups.length);
|
||||
|
||||
final List<ThreadGroup> result = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (predicate.test(threadGroups[i])) {
|
||||
result.add(threadGroups[i]);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableCollection(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all active threadgroups which match the given predicate.
|
||||
*
|
||||
* @param predicate the predicate
|
||||
* @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
|
||||
* @throws IllegalArgumentException if the predicate is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate) {
|
||||
return findThreadGroups(getSystemThreadGroup(), true, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds active thread groups with the specified group name.
|
||||
*
|
||||
* @param threadGroupName The thread group name
|
||||
* @return the thread groups with the specified group name or an empty collection if no such thread group exists. The collection returned is always unmodifiable.
|
||||
* @throws IllegalArgumentException if group name is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<ThreadGroup> findThreadGroupsByName(final String threadGroupName) {
|
||||
return findThreadGroups(new NamePredicate(threadGroupName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
|
||||
*
|
||||
* @param group the thread group
|
||||
* @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
|
||||
* @param predicate the predicate
|
||||
* @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
|
||||
* @throws IllegalArgumentException if the given group or predicate is null
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
|
||||
Validate.notNull(group, "The group must not be null");
|
||||
Validate.notNull(predicate, "The predicate must not be null");
|
||||
|
||||
int count = group.activeCount();
|
||||
Thread[] threads;
|
||||
do {
|
||||
threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size
|
||||
count = group.enumerate(threads, recurse);
|
||||
//return value of enumerate() must be strictly less than the array size according to javadoc
|
||||
} while (count >= threads.length);
|
||||
|
||||
final List<Thread> result = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (predicate.test(threads[i])) {
|
||||
result.add(threads[i]);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableCollection(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all active threads which match the given predicate.
|
||||
*
|
||||
* @param predicate the predicate
|
||||
* @return An unmodifiable {@code Collection} of active threads matching the given predicate
|
||||
*
|
||||
* @throws IllegalArgumentException if the predicate is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<Thread> findThreads(final ThreadPredicate predicate) {
|
||||
return findThreads(getSystemThreadGroup(), true, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds active threads with the specified name.
|
||||
*
|
||||
* @param threadName The thread name
|
||||
* @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable.
|
||||
* @throws IllegalArgumentException if the specified name is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<Thread> findThreadsByName(final String threadName) {
|
||||
return findThreads(new NamePredicate(threadName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds active threads with the specified name if they belong to a thread group with the specified group name.
|
||||
*
|
||||
* @param threadName The thread name
|
||||
* @param threadGroupName The thread group name
|
||||
* @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name,
|
||||
* An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
|
||||
* @throws IllegalArgumentException if the specified thread name or group name is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) {
|
||||
Validate.notNull(threadName, "threadName");
|
||||
Validate.notNull(threadGroupName, "threadGroupName");
|
||||
|
||||
final Collection<ThreadGroup> threadGroups = findThreadGroups(new NamePredicate(threadGroupName));
|
||||
|
||||
if (threadGroups.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Collection<Thread> result = new ArrayList<>();
|
||||
final NamePredicate threadNamePredicate = new NamePredicate(threadName);
|
||||
for (final ThreadGroup group : threadGroups) {
|
||||
result.addAll(findThreads(group, false, threadNamePredicate));
|
||||
}
|
||||
return Collections.unmodifiableCollection(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds active threads with the specified name if they belong to a specified thread group.
|
||||
*
|
||||
* @param threadName The thread name
|
||||
* @param threadGroup The thread group
|
||||
* @return The threads which belongs to a thread group and the thread's name match the specified name,
|
||||
* An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
|
||||
* @throws IllegalArgumentException if the specified thread name or group is null
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) {
|
||||
return findThreads(threadGroup, false, new NamePredicate(threadName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed).
|
||||
*
|
||||
* @return all thread groups excluding the system thread group. The collection returned is always unmodifiable.
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<ThreadGroup> getAllThreadGroups() {
|
||||
return findThreadGroups(ALWAYS_TRUE_PREDICATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all active threads (A thread is active if it has been started and has not yet died).
|
||||
*
|
||||
* @return all active threads. The collection returned is always unmodifiable.
|
||||
* @throws SecurityException
|
||||
* if the current thread cannot access the system thread group
|
||||
*
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static Collection<Thread> getAllThreads() {
|
||||
return findThreads(ALWAYS_TRUE_PREDICATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system thread group (sometimes also referred as "root thread group").
|
||||
*
|
||||
* @return the system thread group
|
||||
* @throws SecurityException if the current thread cannot modify
|
||||
* thread groups from this thread's thread group up to the system thread group
|
||||
*/
|
||||
public static ThreadGroup getSystemThreadGroup() {
|
||||
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||
while (threadGroup.getParent() != null) {
|
||||
threadGroup = threadGroup.getParent();
|
||||
}
|
||||
return threadGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the given thread to die for the given duration. Implemented using {@link Thread#join(long, int)}.
|
||||
*
|
||||
* @param thread The thread to join.
|
||||
* @param duration How long to wait.
|
||||
* @throws InterruptedException if any thread has interrupted the current thread.
|
||||
* @see Thread#join(long, int)
|
||||
* @since 3.12.0
|
||||
*/
|
||||
public static void join(final Thread thread, final Duration duration) throws InterruptedException {
|
||||
DurationUtils.accept(thread::join, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleeps the current thread for the given duration. Implemented using {@link Thread#sleep(long, int)}.
|
||||
*
|
||||
* @param duration How long to sleep.
|
||||
* @throws InterruptedException if any thread has interrupted the current thread.
|
||||
* @see Thread#sleep(long, int)
|
||||
* @since 3.12.0
|
||||
*/
|
||||
public static void sleep(final Duration duration) throws InterruptedException {
|
||||
DurationUtils.accept(Thread::sleep, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
|
||||
* {@code ThreadUtils.getAllThreads()}
|
||||
* </p>
|
||||
* <p>
|
||||
* This constructor is public to permit tools that require a JavaBean instance to operate.
|
||||
* </p>
|
||||
*/
|
||||
public ThreadUtils() {
|
||||
}
|
||||
}
|
1329
before/src/main/java/org/apache/commons/lang3/Validate.java
Normal file
1329
before/src/main/java/org/apache/commons/lang3/Validate.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.arch;
|
||||
|
||||
/**
|
||||
* The {@link Processor} represents a microprocessor and defines
|
||||
* some properties like architecture and type of the microprocessor.
|
||||
*
|
||||
* @since 3.6
|
||||
*/
|
||||
public class Processor {
|
||||
|
||||
/**
|
||||
* The {@link Arch} enum defines the architecture of
|
||||
* a microprocessor. The architecture represents the bit value
|
||||
* of the microprocessor.
|
||||
* The following architectures are defined:
|
||||
* <ul>
|
||||
* <li>32-bit</li>
|
||||
* <li>64-bit</li>
|
||||
* <li>Unknown</li>
|
||||
* </ul>
|
||||
*/
|
||||
public enum Arch {
|
||||
|
||||
/**
|
||||
* A 32-bit processor architecture.
|
||||
*/
|
||||
BIT_32("32-bit"),
|
||||
|
||||
/**
|
||||
* A 64-bit processor architecture.
|
||||
*/
|
||||
BIT_64("64-bit"),
|
||||
|
||||
/**
|
||||
* An unknown-bit processor architecture.
|
||||
*/
|
||||
UNKNOWN("Unknown");
|
||||
|
||||
/**
|
||||
* A label suitable for display.
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
private final String label;
|
||||
|
||||
Arch(final String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label suitable for display.
|
||||
*
|
||||
* @return the label.
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Type} enum defines types of a microprocessor.
|
||||
* The following types are defined:
|
||||
* <ul>
|
||||
* <li>x86</li>
|
||||
* <li>ia64</li>
|
||||
* <li>PPC</li>
|
||||
* <li>Unknown</li>
|
||||
* </ul>
|
||||
*/
|
||||
public enum Type {
|
||||
|
||||
/**
|
||||
* Intel x86 series of instruction set architectures.
|
||||
*/
|
||||
X86,
|
||||
|
||||
/**
|
||||
* Intel Itanium 64-bit architecture.
|
||||
*/
|
||||
IA_64,
|
||||
|
||||
/**
|
||||
* Apple–IBM–Motorola PowerPC architecture.
|
||||
*/
|
||||
PPC,
|
||||
|
||||
/**
|
||||
* Unknown architecture.
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
private final Arch arch;
|
||||
private final Type type;
|
||||
|
||||
/**
|
||||
* Constructs a {@link Processor} object with the given
|
||||
* parameters.
|
||||
*
|
||||
* @param arch The processor architecture.
|
||||
* @param type The processor type.
|
||||
*/
|
||||
public Processor(final Arch arch, final Type type) {
|
||||
this.arch = arch;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the processor architecture as an {@link Arch} enum.
|
||||
* The processor architecture defines, if the processor has
|
||||
* a 32 or 64 bit architecture.
|
||||
*
|
||||
* @return A {@link Arch} enum.
|
||||
*/
|
||||
public Arch getArch() {
|
||||
return arch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the processor type as {@link Type} enum.
|
||||
* The processor type defines, if the processor is for example
|
||||
* a x86 or PPA.
|
||||
*
|
||||
* @return A {@link Type} enum.
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link Processor} is 32 bit.
|
||||
*
|
||||
* @return {@code true}, if {@link Processor} is {@link Arch#BIT_32}, else {@code false}.
|
||||
*/
|
||||
public boolean is32Bit() {
|
||||
return Arch.BIT_32 == arch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link Processor} is 64 bit.
|
||||
*
|
||||
* @return {@code true}, if {@link Processor} is {@link Arch#BIT_64}, else {@code false}.
|
||||
*/
|
||||
public boolean is64Bit() {
|
||||
return Arch.BIT_64 == arch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link Processor} is type of x86.
|
||||
*
|
||||
* @return {@code true}, if {@link Processor} is {@link Type#X86}, else {@code false}.
|
||||
*/
|
||||
public boolean isX86() {
|
||||
return Type.X86 == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link Processor} is type of Intel Itanium.
|
||||
*
|
||||
* @return {@code true}. if {@link Processor} is {@link Type#IA_64}, else {@code false}.
|
||||
*/
|
||||
public boolean isIA64() {
|
||||
return Type.IA_64 == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link Processor} is type of Power PC.
|
||||
*
|
||||
* @return {@code true}. if {@link Processor} is {@link Type#PPC}, else {@code false}.
|
||||
*/
|
||||
public boolean isPPC() {
|
||||
return Type.PPC == type;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Provides classes to work with the values of the os.arch system property.
|
||||
* @since 3.6
|
||||
*/
|
||||
package org.apache.commons.lang3.arch;
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The Builder interface is designed to designate a class as a <em>builder</em>
|
||||
* object in the Builder design pattern. Builders are capable of creating and
|
||||
* configuring objects or results that normally take multiple steps to construct
|
||||
* or are very complex to derive.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The builder interface defines a single method, {@link #build()}, that
|
||||
* classes must implement. The result of this method should be the final
|
||||
* configured object or result after all building operations are performed.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It is a recommended practice that the methods supplied to configure the
|
||||
* object or result being built return a reference to {@code this} so that
|
||||
* method calls can be chained together.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Example Builder:
|
||||
* <pre><code>
|
||||
* class FontBuilder implements Builder<Font> {
|
||||
* private Font font;
|
||||
*
|
||||
* public FontBuilder(String fontName) {
|
||||
* this.font = new Font(fontName, Font.PLAIN, 12);
|
||||
* }
|
||||
*
|
||||
* public FontBuilder bold() {
|
||||
* this.font = this.font.deriveFont(Font.BOLD);
|
||||
* return this; // Reference returned so calls can be chained
|
||||
* }
|
||||
*
|
||||
* public FontBuilder size(float pointSize) {
|
||||
* this.font = this.font.deriveFont(pointSize);
|
||||
* return this; // Reference returned so calls can be chained
|
||||
* }
|
||||
*
|
||||
* // Other Font construction methods
|
||||
*
|
||||
* public Font build() {
|
||||
* return this.font;
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* Example Builder Usage:
|
||||
* <pre><code>
|
||||
* Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
|
||||
* .size(14.0f)
|
||||
* .build();
|
||||
* </code></pre>
|
||||
*
|
||||
*
|
||||
* @param <T> the type of object that the builder will construct or compute.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Builder<T> {
|
||||
|
||||
/**
|
||||
* Returns a reference to the object being constructed or result being
|
||||
* calculated by the builder.
|
||||
*
|
||||
* @return the object constructed or result calculated by the builder.
|
||||
*/
|
||||
T build();
|
||||
}
|
File diff suppressed because it is too large
Load diff
117
before/src/main/java/org/apache/commons/lang3/builder/Diff.java
Normal file
117
before/src/main/java/org/apache/commons/lang3/builder/Diff.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code Diff} contains the differences between two {@link Diffable} class
|
||||
* fields.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Typically, {@code Diff}s are retrieved by using a {@link DiffBuilder} to
|
||||
* produce a {@link DiffResult}, containing the differences between two objects.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param <T>
|
||||
* The type of object contained within this {@code Diff}. Differences
|
||||
* between primitive objects are stored as their Object wrapper
|
||||
* equivalent.
|
||||
* @since 3.3
|
||||
*/
|
||||
public abstract class Diff<T> extends Pair<T, T> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Type type;
|
||||
private final String fieldName;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructs a new {@code Diff} for the given field name.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the name of the field
|
||||
*/
|
||||
protected Diff(final String fieldName) {
|
||||
this.type = ObjectUtils.defaultIfNull(
|
||||
TypeUtils.getTypeArguments(getClass(), Diff.class).get(
|
||||
Diff.class.getTypeParameters()[0]), Object.class);
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the type of the field.
|
||||
* </p>
|
||||
*
|
||||
* @return the field type
|
||||
*/
|
||||
public final Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the name of the field.
|
||||
* </p>
|
||||
*
|
||||
* @return the field name
|
||||
*/
|
||||
public final String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a {@code String} representation of the {@code Diff}, with the
|
||||
* following format:</p>
|
||||
*
|
||||
* <pre>
|
||||
* [fieldname: left-value, right-value]
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
@Override
|
||||
public final String toString() {
|
||||
return String.format("[%s: %s, %s]", fieldName, getLeft(), getRight());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Throws {@code UnsupportedOperationException}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* ignored
|
||||
* @return nothing
|
||||
*/
|
||||
@Override
|
||||
public final T setValue(final T value) {
|
||||
throw new UnsupportedOperationException("Cannot alter Diff object.");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,983 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Assists in implementing {@link Diffable#diff(Object)} methods.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To use this class, write code as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Person implements Diffable<Person> {
|
||||
* String name;
|
||||
* int age;
|
||||
* boolean smoker;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* public DiffResult diff(Person obj) {
|
||||
* // No need for null check, as NullPointerException correct if obj is null
|
||||
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
* .append("name", this.name, obj.name)
|
||||
* .append("age", this.age, obj.age)
|
||||
* .append("smoker", this.smoker, obj.smoker)
|
||||
* .build();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The {@code ToStringStyle} passed to the constructor is embedded in the
|
||||
* returned {@code DiffResult} and influences the style of the
|
||||
* {@code DiffResult.toString()} method. This style choice can be overridden by
|
||||
* calling {@link DiffResult#toString(ToStringStyle)}.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> type of the left and right object.
|
||||
* @see Diffable
|
||||
* @see Diff
|
||||
* @see DiffResult
|
||||
* @see ToStringStyle
|
||||
* @since 3.3
|
||||
*/
|
||||
public class DiffBuilder<T> implements Builder<DiffResult<T>> {
|
||||
|
||||
private final List<Diff<?>> diffs;
|
||||
private final boolean objectsTriviallyEqual;
|
||||
private final T left;
|
||||
private final T right;
|
||||
private final ToStringStyle style;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructs a builder for the specified objects with the specified style.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
|
||||
* not evaluate any calls to {@code append(...)} and will return an empty
|
||||
* {@link DiffResult} when {@link #build()} is executed.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs
|
||||
* {@code this} object
|
||||
* @param rhs
|
||||
* the object to diff against
|
||||
* @param style
|
||||
* the style will use when outputting the objects, {@code null}
|
||||
* uses the default
|
||||
* @param testTriviallyEqual
|
||||
* If true, this will test if lhs and rhs are the same or equal.
|
||||
* All of the append(fieldName, lhs, rhs) methods will abort
|
||||
* without creating a field {@link Diff} if the trivially equal
|
||||
* test is enabled and returns true. The result of this test
|
||||
* is never changed throughout the life of this {@link DiffBuilder}.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code lhs} or {@code rhs} is {@code null}
|
||||
* @since 3.4
|
||||
*/
|
||||
public DiffBuilder(final T lhs, final T rhs,
|
||||
final ToStringStyle style, final boolean testTriviallyEqual) {
|
||||
|
||||
Validate.notNull(lhs, "lhs");
|
||||
Validate.notNull(rhs, "rhs");
|
||||
|
||||
this.diffs = new ArrayList<>();
|
||||
this.left = lhs;
|
||||
this.right = rhs;
|
||||
this.style = style;
|
||||
|
||||
// Don't compare any fields if objects equal
|
||||
this.objectsTriviallyEqual = testTriviallyEqual && (lhs == rhs || lhs.equals(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructs a builder for the specified objects with the specified style.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
|
||||
* not evaluate any calls to {@code append(...)} and will return an empty
|
||||
* {@link DiffResult} when {@link #build()} is executed.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
|
||||
* with the testTriviallyEqual flag enabled.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs
|
||||
* {@code this} object
|
||||
* @param rhs
|
||||
* the object to diff against
|
||||
* @param style
|
||||
* the style will use when outputting the objects, {@code null}
|
||||
* uses the default
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code lhs} or {@code rhs} is {@code null}
|
||||
*/
|
||||
public DiffBuilder(final T lhs, final T rhs,
|
||||
final ToStringStyle style) {
|
||||
|
||||
this(lhs, rhs, style, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code boolean}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code boolean}
|
||||
* @param rhs
|
||||
* the right hand {@code boolean}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final boolean lhs,
|
||||
final boolean rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs != rhs) {
|
||||
diffs.add(new Diff<Boolean>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Boolean getLeft() {
|
||||
return Boolean.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getRight() {
|
||||
return Boolean.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code boolean[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code boolean[]}
|
||||
* @param rhs
|
||||
* the right hand {@code boolean[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final boolean[] lhs,
|
||||
final boolean[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Boolean[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Boolean[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code byte}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code byte}
|
||||
* @param rhs
|
||||
* the right hand {@code byte}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final byte lhs,
|
||||
final byte rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs != rhs) {
|
||||
diffs.add(new Diff<Byte>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Byte getLeft() {
|
||||
return Byte.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getRight() {
|
||||
return Byte.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code byte[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code byte[]}
|
||||
* @param rhs
|
||||
* the right hand {@code byte[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final byte[] lhs,
|
||||
final byte[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Byte[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Byte[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code char}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code char}
|
||||
* @param rhs
|
||||
* the right hand {@code char}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final char lhs,
|
||||
final char rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs != rhs) {
|
||||
diffs.add(new Diff<Character>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Character getLeft() {
|
||||
return Character.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character getRight() {
|
||||
return Character.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code char[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code char[]}
|
||||
* @param rhs
|
||||
* the right hand {@code char[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final char[] lhs,
|
||||
final char[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Character[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Character[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code double}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code double}
|
||||
* @param rhs
|
||||
* the right hand {@code double}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final double lhs,
|
||||
final double rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) {
|
||||
diffs.add(new Diff<Double>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Double getLeft() {
|
||||
return Double.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRight() {
|
||||
return Double.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code double[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code double[]}
|
||||
* @param rhs
|
||||
* the right hand {@code double[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final double[] lhs,
|
||||
final double[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Double[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Double[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code float}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code float}
|
||||
* @param rhs
|
||||
* the right hand {@code float}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final float lhs,
|
||||
final float rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) {
|
||||
diffs.add(new Diff<Float>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Float getLeft() {
|
||||
return Float.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getRight() {
|
||||
return Float.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code float[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code float[]}
|
||||
* @param rhs
|
||||
* the right hand {@code float[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final float[] lhs,
|
||||
final float[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Float[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Float[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code int}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code int}
|
||||
* @param rhs
|
||||
* the right hand {@code int}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final int lhs,
|
||||
final int rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs != rhs) {
|
||||
diffs.add(new Diff<Integer>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Integer getLeft() {
|
||||
return Integer.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getRight() {
|
||||
return Integer.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code int[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code int[]}
|
||||
* @param rhs
|
||||
* the right hand {@code int[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final int[] lhs,
|
||||
final int[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Integer[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Integer[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code long}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code long}
|
||||
* @param rhs
|
||||
* the right hand {@code long}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final long lhs,
|
||||
final long rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs != rhs) {
|
||||
diffs.add(new Diff<Long>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Long getLeft() {
|
||||
return Long.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getRight() {
|
||||
return Long.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code long[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code long[]}
|
||||
* @param rhs
|
||||
* the right hand {@code long[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final long[] lhs,
|
||||
final long[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Long[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Long[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code short}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code short}
|
||||
* @param rhs
|
||||
* the right hand {@code short}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final short lhs,
|
||||
final short rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs != rhs) {
|
||||
diffs.add(new Diff<Short>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Short getLeft() {
|
||||
return Short.valueOf(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short getRight() {
|
||||
return Short.valueOf(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code short[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code short[]}
|
||||
* @param rhs
|
||||
* the right hand {@code short[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final short[] lhs,
|
||||
final short[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Short[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Short[] getLeft() {
|
||||
return ArrayUtils.toObject(lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short[] getRight() {
|
||||
return ArrayUtils.toObject(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code Objects}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code Object}
|
||||
* @param rhs
|
||||
* the right hand {@code Object}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final Object lhs,
|
||||
final Object rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
if (lhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final Object objectToTest;
|
||||
if (lhs != null) {
|
||||
objectToTest = lhs;
|
||||
} else {
|
||||
// rhs cannot be null, as lhs != rhs
|
||||
objectToTest = rhs;
|
||||
}
|
||||
|
||||
if (objectToTest.getClass().isArray()) {
|
||||
if (objectToTest instanceof boolean[]) {
|
||||
return append(fieldName, (boolean[]) lhs, (boolean[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof byte[]) {
|
||||
return append(fieldName, (byte[]) lhs, (byte[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof char[]) {
|
||||
return append(fieldName, (char[]) lhs, (char[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof double[]) {
|
||||
return append(fieldName, (double[]) lhs, (double[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof float[]) {
|
||||
return append(fieldName, (float[]) lhs, (float[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof int[]) {
|
||||
return append(fieldName, (int[]) lhs, (int[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof long[]) {
|
||||
return append(fieldName, (long[]) lhs, (long[]) rhs);
|
||||
}
|
||||
if (objectToTest instanceof short[]) {
|
||||
return append(fieldName, (short[]) lhs, (short[]) rhs);
|
||||
}
|
||||
|
||||
return append(fieldName, (Object[]) lhs, (Object[]) rhs);
|
||||
}
|
||||
|
||||
// Not array type
|
||||
if (lhs != null && lhs.equals(rhs)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
diffs.add(new Diff<Object>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Object getLeft() {
|
||||
return lhs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRight() {
|
||||
return rhs;
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two {@code Object[]}s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param lhs
|
||||
* the left hand {@code Object[]}
|
||||
* @param rhs
|
||||
* the right hand {@code Object[]}
|
||||
* @return this
|
||||
* @throws IllegalArgumentException
|
||||
* if field name is {@code null}
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName, final Object[] lhs,
|
||||
final Object[] rhs) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(lhs, rhs)) {
|
||||
diffs.add(new Diff<Object[]>(fieldName) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Object[] getLeft() {
|
||||
return lhs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getRight() {
|
||||
return rhs;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append diffs from another {@code DiffResult}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method is useful if you want to compare properties which are
|
||||
* themselves Diffable and would like to know which specific part of
|
||||
* it is different.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Person implements Diffable<Person> {
|
||||
* String name;
|
||||
* Address address; // implements Diffable<Address>
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* public DiffResult diff(Person obj) {
|
||||
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
* .append("name", this.name, obj.name)
|
||||
* .append("address", this.address.diff(obj.address))
|
||||
* .build();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param fieldName
|
||||
* the field name
|
||||
* @param diffResult
|
||||
* the {@code DiffResult} to append
|
||||
* @return this
|
||||
* @throws NullPointerException if field name is {@code null}
|
||||
* @since 3.5
|
||||
*/
|
||||
public DiffBuilder<T> append(final String fieldName,
|
||||
final DiffResult<T> diffResult) {
|
||||
validateFieldNameNotNull(fieldName);
|
||||
Validate.notNull(diffResult, "diffResult");
|
||||
if (objectsTriviallyEqual) {
|
||||
return this;
|
||||
}
|
||||
|
||||
for (final Diff<?> diff : diffResult.getDiffs()) {
|
||||
append(fieldName + "." + diff.getFieldName(),
|
||||
diff.getLeft(), diff.getRight());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@link DiffResult} based on the differences appended to this
|
||||
* builder.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@code DiffResult} containing the differences between the two
|
||||
* objects.
|
||||
*/
|
||||
@Override
|
||||
public DiffResult<T> build() {
|
||||
return new DiffResult<>(left, right, diffs, style);
|
||||
}
|
||||
|
||||
private void validateFieldNameNotNull(final String fieldName) {
|
||||
Validate.notNull(fieldName, "fieldName");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code DiffResult} contains a collection of the differences between two
|
||||
* {@link Diffable} objects. Typically these differences are displayed using
|
||||
* {@link #toString()} method, which returns a string describing the fields that
|
||||
* differ between the objects.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use a {@link DiffBuilder} to build a {@code DiffResult} comparing two objects.
|
||||
* </p>
|
||||
* @param <T> type of the left and right object.
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
public class DiffResult<T> implements Iterable<Diff<?>> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The {@code String} returned when the objects have no differences:
|
||||
* {@value}
|
||||
* </p>
|
||||
*/
|
||||
public static final String OBJECTS_SAME_STRING = "";
|
||||
|
||||
private static final String DIFFERS_STRING = "differs from";
|
||||
|
||||
private final List<Diff<?>> diffList;
|
||||
private final T lhs;
|
||||
private final T rhs;
|
||||
private final ToStringStyle style;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a {@link DiffResult} containing the differences between two
|
||||
* objects.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs
|
||||
* the left hand object
|
||||
* @param rhs
|
||||
* the right hand object
|
||||
* @param diffList
|
||||
* the list of differences, may be empty
|
||||
* @param style
|
||||
* the style to use for the {@link #toString()} method. May be
|
||||
* {@code null}, in which case
|
||||
* {@link ToStringStyle#DEFAULT_STYLE} is used
|
||||
* @throws NullPointerException if {@code lhs}, {@code rhs} or {@code diffs} is {@code null}
|
||||
*/
|
||||
DiffResult(final T lhs, final T rhs, final List<Diff<?>> diffList,
|
||||
final ToStringStyle style) {
|
||||
Validate.notNull(lhs, "lhs");
|
||||
Validate.notNull(rhs, "rhs");
|
||||
Validate.notNull(diffList, "diffList");
|
||||
|
||||
this.diffList = diffList;
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
|
||||
if (style == null) {
|
||||
this.style = ToStringStyle.DEFAULT_STYLE;
|
||||
} else {
|
||||
this.style = style;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the object the right object has been compared to.</p>
|
||||
*
|
||||
* @return the left object of the diff
|
||||
* @since 3.10
|
||||
*/
|
||||
public T getLeft() {
|
||||
return this.lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the object the left object has been compared to.</p>
|
||||
*
|
||||
* @return the right object of the diff
|
||||
* @since 3.10
|
||||
*/
|
||||
public T getRight() {
|
||||
return this.rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns an unmodifiable list of {@code Diff}s. The list may be empty if
|
||||
* there were no differences between the objects.
|
||||
* </p>
|
||||
*
|
||||
* @return an unmodifiable list of {@code Diff}s
|
||||
*/
|
||||
public List<Diff<?>> getDiffs() {
|
||||
return Collections.unmodifiableList(diffList);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the number of differences between the two objects.
|
||||
* </p>
|
||||
*
|
||||
* @return the number of differences
|
||||
*/
|
||||
public int getNumberOfDiffs() {
|
||||
return diffList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the style used by the {@link #toString()} method.
|
||||
* </p>
|
||||
*
|
||||
* @return the style
|
||||
*/
|
||||
public ToStringStyle getToStringStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code String} description of the differences contained within
|
||||
* this {@code DiffResult}. A {@link ToStringBuilder} is used for each object
|
||||
* and the style of the output is governed by the {@code ToStringStyle}
|
||||
* passed to the constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If there are no differences stored in this list, the method will return
|
||||
* {@link #OBJECTS_SAME_STRING}. Otherwise, using the example given in
|
||||
* {@link Diffable} and {@link ToStringStyle#SHORT_PREFIX_STYLE}, an output
|
||||
* might be:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* Person[name=John Doe,age=32] differs from Person[name=Joe Bloggs,age=26]
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This indicates that the objects differ in name and age, but not in
|
||||
* smoking status.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To use a different {@code ToStringStyle} for an instance of this class,
|
||||
* use {@link #toString(ToStringStyle)}.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@code String} description of the differences.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code String} description of the differences contained within
|
||||
* this {@code DiffResult}, using the supplied {@code ToStringStyle}.
|
||||
* </p>
|
||||
*
|
||||
* @param style
|
||||
* the {@code ToStringStyle} to use when outputting the objects
|
||||
*
|
||||
* @return a {@code String} description of the differences.
|
||||
*/
|
||||
public String toString(final ToStringStyle style) {
|
||||
if (diffList.isEmpty()) {
|
||||
return OBJECTS_SAME_STRING;
|
||||
}
|
||||
|
||||
final ToStringBuilder lhsBuilder = new ToStringBuilder(lhs, style);
|
||||
final ToStringBuilder rhsBuilder = new ToStringBuilder(rhs, style);
|
||||
|
||||
for (final Diff<?> diff : diffList) {
|
||||
lhsBuilder.append(diff.getFieldName(), diff.getLeft());
|
||||
rhsBuilder.append(diff.getFieldName(), diff.getRight());
|
||||
}
|
||||
|
||||
return String.format("%s %s %s", lhsBuilder.build(), DIFFERS_STRING,
|
||||
rhsBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns an iterator over the {@code Diff} objects contained in this list.
|
||||
* </p>
|
||||
*
|
||||
* @return the iterator
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Diff<?>> iterator() {
|
||||
return diffList.iterator();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
/**
|
||||
* <p>{@code Diffable} classes can be compared with other objects
|
||||
* for differences. The {@link DiffResult} object retrieved can be queried
|
||||
* for a list of differences or printed using the {@link DiffResult#toString()}.</p>
|
||||
*
|
||||
* <p>The calculation of the differences is <i>consistent with equals</i> if
|
||||
* and only if {@code d1.equals(d2)} implies {@code d1.diff(d2) == ""}.
|
||||
* It is strongly recommended that implementations are consistent with equals
|
||||
* to avoid confusion. Note that {@code null} is not an instance of any class
|
||||
* and {@code d1.diff(null)} should throw a {@code NullPointerException}.</p>
|
||||
*
|
||||
* <p>
|
||||
* {@code Diffable} classes lend themselves well to unit testing, in which a
|
||||
* easily readable description of the differences between an anticipated result and
|
||||
* an actual result can be retrieved. For example:
|
||||
* </p>
|
||||
* <pre>
|
||||
* Assert.assertEquals(expected.diff(result), expected, result);
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> the type of objects that this object may be differentiated against
|
||||
* @since 3.3
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Diffable<T> {
|
||||
|
||||
/**
|
||||
* <p>Retrieves a list of the differences between
|
||||
* this object and the supplied object.</p>
|
||||
*
|
||||
* @param obj the object to diff against, can be {@code null}
|
||||
* @return a list of differences
|
||||
* @throws NullPointerException if the specified object is {@code null}
|
||||
*/
|
||||
DiffResult<T> diff(T obj);
|
||||
}
|
File diff suppressed because it is too large
Load diff
36
before/src/main/java/org/apache/commons/lang3/builder/EqualsExclude.java
Executable file
36
before/src/main/java/org/apache/commons/lang3/builder/EqualsExclude.java
Executable file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Use this annotation to exclude a field from being used by
|
||||
* the various {@code reflectionEquals} methods defined on
|
||||
* {@link EqualsBuilder}.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface EqualsExclude {
|
||||
// empty
|
||||
}
|
|
@ -0,0 +1,994 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ArraySorter;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Assists in implementing {@link Object#hashCode()} methods.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This class enables a good {@code hashCode} method to be built for any class. It follows the rules laid out in
|
||||
* the book <a href="http://www.oracle.com/technetwork/java/effectivejava-136174.html">Effective Java</a> by Joshua Bloch. Writing a
|
||||
* good {@code hashCode} method is actually quite difficult. This class aims to simplify the process.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The following is the approach taken. When appending a data field, the current total is multiplied by the
|
||||
* multiplier then a relevant value
|
||||
* for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then
|
||||
* appending the integer 45 will create a hash code of 674, namely 17 * 37 + 45.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* All relevant fields from the object should be included in the {@code hashCode} method. Derived fields may be
|
||||
* excluded. In general, any field used in the {@code equals} method must be used in the {@code hashCode}
|
||||
* method.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To use this class write code as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Person {
|
||||
* String name;
|
||||
* int age;
|
||||
* boolean smoker;
|
||||
* ...
|
||||
*
|
||||
* public int hashCode() {
|
||||
* // you pick a hard-coded, randomly chosen, non-zero, odd number
|
||||
* // ideally different for each class
|
||||
* return new HashCodeBuilder(17, 37).
|
||||
* append(name).
|
||||
* append(age).
|
||||
* append(smoker).
|
||||
* toHashCode();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* If required, the superclass {@code hashCode()} can be added using {@link #appendSuper}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
|
||||
* usually private, the method, {@code reflectionHashCode}, uses {@code AccessibleObject.setAccessible}
|
||||
* to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
|
||||
* are set up correctly. It is also slower than testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A typical invocation for this method would look like:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public int hashCode() {
|
||||
* return HashCodeBuilder.reflectionHashCode(this);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>The {@link HashCodeExclude} annotation can be used to exclude fields from being
|
||||
* used by the {@code reflectionHashCode} methods.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public class HashCodeBuilder implements Builder<Integer> {
|
||||
/**
|
||||
* The default initial value to use in reflection hash code building.
|
||||
*/
|
||||
private static final int DEFAULT_INITIAL_VALUE = 17;
|
||||
|
||||
/**
|
||||
* The default multiplier value to use in reflection hash code building.
|
||||
*/
|
||||
private static final int DEFAULT_MULTIPLIER_VALUE = 37;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<>();
|
||||
|
||||
/*
|
||||
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
||||
* we are in the process of calculating.
|
||||
*
|
||||
* So we generate a one-to-one mapping from the original object to a new object.
|
||||
*
|
||||
* Now HashSet uses equals() to determine if two elements with the same hash code really
|
||||
* are equal, so we also need to ensure that the replacement objects are only equal
|
||||
* if the original objects are identical.
|
||||
*
|
||||
* The original implementation (2.4 and before) used the System.identityHashCode()
|
||||
* method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
|
||||
*
|
||||
* We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
|
||||
* to disambiguate the duplicate ids.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the registry of objects being traversed by the reflection methods in the current thread.
|
||||
* </p>
|
||||
*
|
||||
* @return Set the registry of objects being traversed
|
||||
* @since 2.3
|
||||
*/
|
||||
static Set<IDKey> getRegistry() {
|
||||
return REGISTRY.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns {@code true} if the registry contains the given object. Used by the reflection methods to avoid
|
||||
* infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* The object to lookup in the registry.
|
||||
* @return boolean {@code true} if the registry contains the given object.
|
||||
* @since 2.3
|
||||
*/
|
||||
static boolean isRegistered(final Object value) {
|
||||
final Set<IDKey> registry = getRegistry();
|
||||
return registry != null && registry.contains(new IDKey(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Appends the fields and values defined by the given object of the given {@code Class}.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the object to append details of
|
||||
* @param clazz
|
||||
* the class to append details of
|
||||
* @param builder
|
||||
* the builder to append to
|
||||
* @param useTransients
|
||||
* whether to use transient fields
|
||||
* @param excludeFields
|
||||
* Collection of String field names to exclude from use in calculation of hash code
|
||||
*/
|
||||
private static void reflectionAppend(final Object object, final Class<?> clazz, final HashCodeBuilder builder, final boolean useTransients,
|
||||
final String[] excludeFields) {
|
||||
if (isRegistered(object)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
register(object);
|
||||
// The elements in the returned array are not sorted and are not in any particular order.
|
||||
final Field[] fields = ArraySorter.sort(clazz.getDeclaredFields(), Comparator.comparing(Field::getName));
|
||||
AccessibleObject.setAccessible(fields, true);
|
||||
for (final Field field : fields) {
|
||||
if (!ArrayUtils.contains(excludeFields, field.getName())
|
||||
&& !field.getName().contains("$")
|
||||
&& (useTransients || !Modifier.isTransient(field.getModifiers()))
|
||||
&& !Modifier.isStatic(field.getModifiers())
|
||||
&& !field.isAnnotationPresent(HashCodeExclude.class)) {
|
||||
try {
|
||||
final Object fieldValue = field.get(object);
|
||||
builder.append(fieldValue);
|
||||
} catch (final IllegalAccessException e) {
|
||||
// this can't happen. Would get a Security exception instead
|
||||
// throw a runtime exception in case the impossible happens.
|
||||
throw new InternalError("Unexpected IllegalAccessException");
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unregister(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||
* {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value. This will be the returned
|
||||
* value if no fields are found to include in the hash code
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @param object
|
||||
* the Object to create a {@code hashCode} for
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
*
|
||||
* @see HashCodeExclude
|
||||
*/
|
||||
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object) {
|
||||
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the TestTransients parameter is set to {@code true}, transient members will be tested, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value. This will be the returned
|
||||
* value if no fields are found to include in the hash code
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @param object
|
||||
* the Object to create a {@code hashCode} for
|
||||
* @param testTransients
|
||||
* whether to include transient fields
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
*
|
||||
* @see HashCodeExclude
|
||||
*/
|
||||
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object,
|
||||
final boolean testTransients) {
|
||||
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the TestTransients parameter is set to {@code true}, transient members will be tested, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be included. Superclass fields will be included up to and including the specified
|
||||
* superclass. A null superclass is treated as java.lang.Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object involved
|
||||
* @param initialNonZeroOddNumber
|
||||
* a non-zero, odd number used as the initial value. This will be the returned
|
||||
* value if no fields are found to include in the hash code
|
||||
* @param multiplierNonZeroOddNumber
|
||||
* a non-zero, odd number used as the multiplier
|
||||
* @param object
|
||||
* the Object to create a {@code hashCode} for
|
||||
* @param testTransients
|
||||
* whether to include transient fields
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||
* @param excludeFields
|
||||
* array of field names to exclude from use in calculation of hash code
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is zero or even
|
||||
*
|
||||
* @see HashCodeExclude
|
||||
* @since 2.0
|
||||
*/
|
||||
public static <T> int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final T object,
|
||||
final boolean testTransients, final Class<? super T> reflectUpToClass, final String... excludeFields) {
|
||||
Validate.notNull(object, "object");
|
||||
final HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
|
||||
Class<?> clazz = object.getClass();
|
||||
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||
while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
|
||||
clazz = clazz.getSuperclass();
|
||||
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||
}
|
||||
return builder.toHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* If the TestTransients parameter is set to {@code true}, transient members will be tested, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||
* in the hash code, the result of this method will be constant.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to create a {@code hashCode} for
|
||||
* @param testTransients
|
||||
* whether to include transient fields
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the object is {@code null}
|
||||
*
|
||||
* @see HashCodeExclude
|
||||
*/
|
||||
public static int reflectionHashCode(final Object object, final boolean testTransients) {
|
||||
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object,
|
||||
testTransients, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||
* {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||
* in the hash code, the result of this method will be constant.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to create a {@code hashCode} for
|
||||
* @param excludeFields
|
||||
* Collection of String field names to exclude from use in calculation of hash code
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the object is {@code null}
|
||||
*
|
||||
* @see HashCodeExclude
|
||||
*/
|
||||
public static int reflectionHashCode(final Object object, final Collection<String> excludeFields) {
|
||||
return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||
* {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||
* in the hash code, the result of this method will be constant.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to create a {@code hashCode} for
|
||||
* @param excludeFields
|
||||
* array of field names to exclude from use in calculation of hash code
|
||||
* @return int hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the object is {@code null}
|
||||
*
|
||||
* @see HashCodeExclude
|
||||
*/
|
||||
public static int reflectionHashCode(final Object object, final String... excludeFields) {
|
||||
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object, false,
|
||||
null, excludeFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers the given object. Used by the reflection methods to avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* The object to register.
|
||||
*/
|
||||
private static void register(final Object value) {
|
||||
Set<IDKey> registry = getRegistry();
|
||||
if (registry == null) {
|
||||
registry = new HashSet<>();
|
||||
REGISTRY.set(registry);
|
||||
}
|
||||
registry.add(new IDKey(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Unregisters the given object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
*
|
||||
* @param value
|
||||
* The object to unregister.
|
||||
* @since 2.3
|
||||
*/
|
||||
private static void unregister(final Object value) {
|
||||
final Set<IDKey> registry = getRegistry();
|
||||
if (registry != null) {
|
||||
registry.remove(new IDKey(value));
|
||||
if (registry.isEmpty()) {
|
||||
REGISTRY.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant to use in building the hashCode.
|
||||
*/
|
||||
private final int iConstant;
|
||||
|
||||
/**
|
||||
* Running total of the hashCode.
|
||||
*/
|
||||
private int iTotal;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Uses two hard coded choices for the constants needed to build a {@code hashCode}.
|
||||
* </p>
|
||||
*/
|
||||
public HashCodeBuilder() {
|
||||
iConstant = 37;
|
||||
iTotal = 17;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Two randomly chosen, odd numbers must be passed in. Ideally these should be different for each class,
|
||||
* however this is not vital.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Prime numbers are preferred, especially for the multiplier.
|
||||
* </p>
|
||||
*
|
||||
* @param initialOddNumber
|
||||
* an odd number used as the initial value
|
||||
* @param multiplierOddNumber
|
||||
* an odd number used as the multiplier
|
||||
* @throws IllegalArgumentException
|
||||
* if the number is even
|
||||
*/
|
||||
public HashCodeBuilder(final int initialOddNumber, final int multiplierOddNumber) {
|
||||
Validate.isTrue(initialOddNumber % 2 != 0, "HashCodeBuilder requires an odd initial value");
|
||||
Validate.isTrue(multiplierOddNumber % 2 != 0, "HashCodeBuilder requires an odd multiplier");
|
||||
iConstant = multiplierOddNumber;
|
||||
iTotal = initialOddNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code boolean}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This adds {@code 1} when true, and {@code 0} when false to the {@code hashCode}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is in contrast to the standard {@code java.lang.Boolean.hashCode} handling, which computes
|
||||
* a {@code hashCode} value of {@code 1231} for {@code java.lang.Boolean} instances
|
||||
* that represent {@code true} or {@code 1237} for {@code java.lang.Boolean} instances
|
||||
* that represent {@code false}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is in accordance with the <i>Effective Java</i> design.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the boolean to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final boolean value) {
|
||||
iTotal = iTotal * iConstant + (value ? 0 : 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code boolean} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final boolean[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final boolean element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code byte}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the byte to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final byte value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code byte} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final byte[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final byte element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code char}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the char to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final char value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code char} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final char[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final char element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code double}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the double to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final double value) {
|
||||
return append(Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code double} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final double[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final double element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code float}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the float to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final float value) {
|
||||
iTotal = iTotal * iConstant + Float.floatToIntBits(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code float} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final float[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final float element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for an {@code int}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the int to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final int value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for an {@code int} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final int[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final int element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code long}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the long to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
// NOTE: This method uses >> and not >>> as Effective Java and
|
||||
// Long.hashCode do. Ideally we should switch to >>> at
|
||||
// some stage. There are backwards compat issues, so
|
||||
// that will have to wait for the time being. cf LANG-342.
|
||||
public HashCodeBuilder append(final long value) {
|
||||
iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code long} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final long[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final long element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for an {@code Object}.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final Object object) {
|
||||
if (object == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
|
||||
} else if (object.getClass().isArray()) {
|
||||
// factor out array case in order to keep method small enough
|
||||
// to be inlined
|
||||
appendArray(object);
|
||||
} else {
|
||||
iTotal = iTotal * iConstant + object.hashCode();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for an array.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the array to add to the {@code hashCode}
|
||||
*/
|
||||
private void appendArray(final Object object) {
|
||||
// 'Switch' on type of array, to dispatch to the correct handler
|
||||
// This handles multi dimensional arrays
|
||||
if (object instanceof long[]) {
|
||||
append((long[]) object);
|
||||
} else if (object instanceof int[]) {
|
||||
append((int[]) object);
|
||||
} else if (object instanceof short[]) {
|
||||
append((short[]) object);
|
||||
} else if (object instanceof char[]) {
|
||||
append((char[]) object);
|
||||
} else if (object instanceof byte[]) {
|
||||
append((byte[]) object);
|
||||
} else if (object instanceof double[]) {
|
||||
append((double[]) object);
|
||||
} else if (object instanceof float[]) {
|
||||
append((float[]) object);
|
||||
} else if (object instanceof boolean[]) {
|
||||
append((boolean[]) object);
|
||||
} else {
|
||||
// Not an array of primitives
|
||||
append((Object[]) object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for an {@code Object} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final Object[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final Object element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code short}.
|
||||
* </p>
|
||||
*
|
||||
* @param value
|
||||
* the short to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final short value) {
|
||||
iTotal = iTotal * iConstant + value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append a {@code hashCode} for a {@code short} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code hashCode}
|
||||
* @return this
|
||||
*/
|
||||
public HashCodeBuilder append(final short[] array) {
|
||||
if (array == null) {
|
||||
iTotal = iTotal * iConstant;
|
||||
} else {
|
||||
for (final short element : array) {
|
||||
append(element);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds the result of super.hashCode() to this builder.
|
||||
* </p>
|
||||
*
|
||||
* @param superHashCode
|
||||
* the result of calling {@code super.hashCode()}
|
||||
* @return this HashCodeBuilder, used to chain calls.
|
||||
* @since 2.0
|
||||
*/
|
||||
public HashCodeBuilder appendSuper(final int superHashCode) {
|
||||
iTotal = iTotal * iConstant + superHashCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the computed {@code hashCode}.
|
||||
* </p>
|
||||
*
|
||||
* @return {@code hashCode} based on the fields appended
|
||||
*/
|
||||
public int toHashCode() {
|
||||
return iTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computed {@code hashCode}.
|
||||
*
|
||||
* @return {@code hashCode} based on the fields appended
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@Override
|
||||
public Integer build() {
|
||||
return Integer.valueOf(toHashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The computed {@code hashCode} from toHashCode() is returned due to the likelihood
|
||||
* of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for
|
||||
* HashCodeBuilder itself is.</p>
|
||||
*
|
||||
* @return {@code hashCode} based on the fields appended
|
||||
* @since 2.5
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toHashCode();
|
||||
}
|
||||
|
||||
}
|
36
before/src/main/java/org/apache/commons/lang3/builder/HashCodeExclude.java
Executable file
36
before/src/main/java/org/apache/commons/lang3/builder/HashCodeExclude.java
Executable file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Use this annotation to exclude a field from being used by
|
||||
* the various {@code reflectionHashcode} methods defined on
|
||||
* {@link HashCodeBuilder}.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface HashCodeExclude {
|
||||
// empty
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
// adapted from org.apache.axis.utils.IDKey
|
||||
|
||||
/**
|
||||
* Wrap an identity key (System.identityHashCode())
|
||||
* so that an object can only be equal() to itself.
|
||||
*
|
||||
* This is necessary to disambiguate the occasional duplicate
|
||||
* identityHashCodes that can occur.
|
||||
*/
|
||||
final class IDKey {
|
||||
private final Object value;
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Constructor for IDKey
|
||||
* @param _value The value
|
||||
*/
|
||||
IDKey(final Object _value) {
|
||||
// This is the Object hash code
|
||||
id = System.identityHashCode(_value);
|
||||
// There have been some cases (LANG-459) that return the
|
||||
// same identity hash code for different objects. So
|
||||
// the value is also added to disambiguate these cases.
|
||||
value = _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns hash code - i.e. the system identity hashcode.
|
||||
* @return the hashcode
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if instances are equal
|
||||
* @param other The other object to compare to
|
||||
* @return if the instances are for the same object
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!(other instanceof IDKey)) {
|
||||
return false;
|
||||
}
|
||||
final IDKey idKey = (IDKey) other;
|
||||
if (id != idKey.id) {
|
||||
return false;
|
||||
}
|
||||
// Note that identity equals is used.
|
||||
return value == idKey.value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* <p>Works with {@link ToStringBuilder} to create a "deep" {@code toString}.
|
||||
* But instead a single line like the {@link RecursiveToStringStyle} this creates a multiline String
|
||||
* similar to the {@link ToStringStyle#MULTI_LINE_STYLE}.</p>
|
||||
*
|
||||
* <p>To use this class write code as follows:</p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Job {
|
||||
* String title;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* public class Person {
|
||||
* String name;
|
||||
* int age;
|
||||
* boolean smoker;
|
||||
* Job job;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* public String toString() {
|
||||
* return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This will produce a toString of the format:<br>
|
||||
* <code>Person@7f54[ <br>
|
||||
* name=Stephen, <br>
|
||||
* age=29, <br>
|
||||
* smoker=false, <br>
|
||||
* job=Job@43cd2[ <br>
|
||||
* title=Manager <br>
|
||||
* ] <br>
|
||||
* ]
|
||||
* </code>
|
||||
* </p>
|
||||
*
|
||||
* @since 3.4
|
||||
*/
|
||||
public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
|
||||
|
||||
/**
|
||||
* Required for serialization support.
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Indenting of inner lines. */
|
||||
private static final int INDENT = 2;
|
||||
|
||||
/** Current indenting. */
|
||||
private int spaces = 2;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public MultilineRecursiveToStringStyle() {
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the fields responsible for the line breaks and indenting.
|
||||
* Must be invoked after changing the {@link #spaces} value.
|
||||
*/
|
||||
private void resetIndent() {
|
||||
setArrayStart("{" + System.lineSeparator() + spacer(spaces));
|
||||
setArraySeparator("," + System.lineSeparator() + spacer(spaces));
|
||||
setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
|
||||
|
||||
setContentStart("[" + System.lineSeparator() + spacer(spaces));
|
||||
setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
|
||||
setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a StringBuilder responsible for the indenting.
|
||||
*
|
||||
* @param spaces how far to indent
|
||||
* @return a StringBuilder with {spaces} leading space characters.
|
||||
*/
|
||||
private StringBuilder spacer(final int spaces) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < spaces; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
|
||||
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
|
||||
&& accept(value.getClass())) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
buffer.append(ReflectionToStringBuilder.toString(value, this));
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
} else {
|
||||
super.appendDetail(buffer, fieldName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.reflectionAppendArrayDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
|
||||
spaces += INDENT;
|
||||
resetIndent();
|
||||
super.appendDetail(buffer, fieldName, array);
|
||||
spaces -= INDENT;
|
||||
resetIndent();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* <p>Works with {@link ToStringBuilder} to create a "deep" {@code toString}.</p>
|
||||
*
|
||||
* <p>To use this class write code as follows:</p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Job {
|
||||
* String title;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* public class Person {
|
||||
* String name;
|
||||
* int age;
|
||||
* boolean smoker;
|
||||
* Job job;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* public String toString() {
|
||||
* return new ReflectionToStringBuilder(this, new RecursiveToStringStyle()).toString();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This will produce a toString of the format:
|
||||
* {@code Person@7f54[name=Stephen,age=29,smoker=false,job=Job@43cd2[title=Manager]]}</p>
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public class RecursiveToStringStyle extends ToStringStyle {
|
||||
|
||||
/**
|
||||
* Required for serialization support.
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <p>Constructor.</p>
|
||||
*/
|
||||
public RecursiveToStringStyle() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
|
||||
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) &&
|
||||
!String.class.equals(value.getClass()) &&
|
||||
accept(value.getClass())) {
|
||||
buffer.append(ReflectionToStringBuilder.toString(value, this));
|
||||
} else {
|
||||
super.appendDetail(buffer, fieldName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
|
||||
appendClassName(buffer, coll);
|
||||
appendIdentityHashCode(buffer, coll);
|
||||
appendDetail(buffer, fieldName, coll.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not to recursively format the given {@code Class}.
|
||||
* By default, this method always returns {@code true}, but may be overwritten by
|
||||
* sub-classes to filter specific classes.
|
||||
*
|
||||
* @param clazz
|
||||
* The class to test.
|
||||
* @return Whether or not to recursively format the given {@code Class}.
|
||||
*/
|
||||
protected boolean accept(final Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import static org.apache.commons.lang3.reflect.FieldUtils.readField;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Assists in implementing {@link Diffable#diff(Object)} methods.
|
||||
* </p>
|
||||
* <p>
|
||||
* All non-static, non-transient fields (including inherited fields)
|
||||
* of the objects to diff are discovered using reflection and compared
|
||||
* for differences.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To use this class, write code as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class Person implements Diffable<Person> {
|
||||
* String name;
|
||||
* int age;
|
||||
* boolean smoker;
|
||||
* ...
|
||||
*
|
||||
* public DiffResult diff(Person obj) {
|
||||
* // No need for null check, as NullPointerException correct if obj is null
|
||||
* return new ReflectionDiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
* .build();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The {@code ToStringStyle} passed to the constructor is embedded in the
|
||||
* returned {@code DiffResult} and influences the style of the
|
||||
* {@code DiffResult.toString()} method. This style choice can be overridden by
|
||||
* calling {@link DiffResult#toString(ToStringStyle)}.
|
||||
* </p>
|
||||
* @param <T>
|
||||
* type of the left and right object to diff.
|
||||
* @see Diffable
|
||||
* @see Diff
|
||||
* @see DiffResult
|
||||
* @see ToStringStyle
|
||||
* @since 3.6
|
||||
*/
|
||||
public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
||||
|
||||
private final Object left;
|
||||
private final Object right;
|
||||
private final DiffBuilder<T> diffBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructs a builder for the specified objects with the specified style.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
|
||||
* not evaluate any calls to {@code append(...)} and will return an empty
|
||||
* {@link DiffResult} when {@link #build()} is executed.
|
||||
* </p>
|
||||
* @param lhs
|
||||
* {@code this} object
|
||||
* @param rhs
|
||||
* the object to diff against
|
||||
* @param style
|
||||
* the style will use when outputting the objects, {@code null}
|
||||
* uses the default
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code lhs} or {@code rhs} is {@code null}
|
||||
*/
|
||||
public ReflectionDiffBuilder(final T lhs, final T rhs, final ToStringStyle style) {
|
||||
this.left = lhs;
|
||||
this.right = rhs;
|
||||
diffBuilder = new DiffBuilder<>(lhs, rhs, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiffResult<T> build() {
|
||||
if (left.equals(right)) {
|
||||
return diffBuilder.build();
|
||||
}
|
||||
|
||||
appendFields(left.getClass());
|
||||
return diffBuilder.build();
|
||||
}
|
||||
|
||||
private void appendFields(final Class<?> clazz) {
|
||||
for (final Field field : FieldUtils.getAllFields(clazz)) {
|
||||
if (accept(field)) {
|
||||
try {
|
||||
diffBuilder.append(field.getName(), readField(field, left, true),
|
||||
readField(field, right, true));
|
||||
} catch (final IllegalAccessException ex) {
|
||||
//this can't happen. Would get a Security exception instead
|
||||
//throw a runtime exception in case the impossible happens.
|
||||
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean accept(final Field field) {
|
||||
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isTransient(field.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
return !Modifier.isStatic(field.getModifiers());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,848 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ArraySorter;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Assists in implementing {@link Object#toString()} methods using reflection.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class uses reflection to determine the fields to append. Because these fields are usually private, the class
|
||||
* uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to
|
||||
* change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are
|
||||
* set up correctly.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using reflection to access (private) fields circumvents any synchronization protection guarding access to these
|
||||
* fields. If a toString method cannot safely read a field, you should exclude it from the toString method, or use
|
||||
* synchronization consistent with the class' lock management around the invocation of the method. Take special care to
|
||||
* exclude non-thread-safe collection classes, because these classes may throw ConcurrentModificationException if
|
||||
* modified while the toString method is executing.
|
||||
* </p>
|
||||
* <p>
|
||||
* A typical invocation for this method would look like:
|
||||
* </p>
|
||||
* <pre>
|
||||
* public String toString() {
|
||||
* return ReflectionToStringBuilder.toString(this);
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* You can also use the builder to debug 3rd party objects:
|
||||
* </p>
|
||||
* <pre>
|
||||
* System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
|
||||
* </pre>
|
||||
* <p>
|
||||
* A subclass can control field output by overriding the methods:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link #accept(java.lang.reflect.Field)}</li>
|
||||
* <li>{@link #getValue(java.lang.reflect.Field)}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* For example, this method does <i>not</i> include the {@code password} field in the returned {@code String}:
|
||||
* </p>
|
||||
* <pre>
|
||||
* public String toString() {
|
||||
* return (new ReflectionToStringBuilder(this) {
|
||||
* protected boolean accept(Field f) {
|
||||
* return super.accept(f) && !f.getName().equals("password");
|
||||
* }
|
||||
* }).toString();
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* Alternatively the {@link ToStringExclude} annotation can be used to exclude fields from being incorporated in the
|
||||
* result.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is also possible to use the {@link ToStringSummary} annotation to output the summary information instead of the
|
||||
* detailed information of a field.
|
||||
* </p>
|
||||
* <p>
|
||||
* The exact format of the {@code toString} is determined by the {@link ToStringStyle} passed into the constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> the default {@link ToStringStyle} will only do a "shallow" formatting, i.e. composed objects are not
|
||||
* further traversed. To get "deep" formatting, use an instance of {@link RecursiveToStringStyle}.
|
||||
* </p>
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ReflectionToStringBuilder extends ToStringBuilder {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code toString} value using the default {@code ToStringStyle} through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be included, as they are likely derived. Static fields will not be included.
|
||||
* Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
*
|
||||
* @see ToStringExclude
|
||||
* @see ToStringSummary
|
||||
*/
|
||||
public static String toString(final Object object) {
|
||||
return toString(object, null, false, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code toString} value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Transient members will be not be included, as they are likely derived. Static fields will not be included.
|
||||
* Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object or {@code ToStringStyle} is {@code null}
|
||||
*
|
||||
* @see ToStringExclude
|
||||
* @see ToStringSummary
|
||||
*/
|
||||
public static String toString(final Object object, final ToStringStyle style) {
|
||||
return toString(object, style, false, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code toString} value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputTransients} is {@code true}, transient members will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be included. Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
*
|
||||
* @see ToStringExclude
|
||||
* @see ToStringSummary
|
||||
*/
|
||||
public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients) {
|
||||
return toString(object, style, outputTransients, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code toString} value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputTransients} is {@code true}, transient fields will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Static fields will not be included. Superclass fields will be appended.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
*
|
||||
* @see ToStringExclude
|
||||
* @see ToStringSummary
|
||||
* @since 2.1
|
||||
*/
|
||||
public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients, final boolean outputStatics) {
|
||||
return toString(object, style, outputTransients, outputStatics, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code toString} value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputTransients} is {@code true}, transient fields will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
|
||||
* {@code java.lang.Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
*
|
||||
* @see ToStringExclude
|
||||
* @see ToStringSummary
|
||||
* @since 2.1
|
||||
*/
|
||||
public static <T> String toString(
|
||||
final T object, final ToStringStyle style, final boolean outputTransients,
|
||||
final boolean outputStatics, final Class<? super T> reflectUpToClass) {
|
||||
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builds a {@code toString} value through reflection.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||
* also not as efficient as testing explicitly.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputTransients} is {@code true}, transient fields will be output, otherwise they
|
||||
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
|
||||
* {@code java.lang.Object}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object
|
||||
* @param object
|
||||
* the Object to be output
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @param excludeNullValues
|
||||
* whether to exclude fields whose values are null
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||
* @return the String result
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object is {@code null}
|
||||
*
|
||||
* @see ToStringExclude
|
||||
* @see ToStringSummary
|
||||
* @since 3.6
|
||||
*/
|
||||
public static <T> String toString(
|
||||
final T object, final ToStringStyle style, final boolean outputTransients,
|
||||
final boolean outputStatics, final boolean excludeNullValues, final Class<? super T> reflectUpToClass) {
|
||||
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics, excludeNullValues)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a String for a toString method excluding the given field names.
|
||||
*
|
||||
* @param object
|
||||
* The object to "toString".
|
||||
* @param excludeFieldNames
|
||||
* The field names to exclude. Null excludes nothing.
|
||||
* @return The toString value.
|
||||
*/
|
||||
public static String toStringExclude(final Object object, final Collection<String> excludeFieldNames) {
|
||||
return toStringExclude(object, toNoNullStringArray(excludeFieldNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given Collection into an array of Strings. The returned array does not contain {@code null}
|
||||
* entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element
|
||||
* is {@code null}.
|
||||
*
|
||||
* @param collection
|
||||
* The collection to convert
|
||||
* @return A new array of Strings.
|
||||
*/
|
||||
static String[] toNoNullStringArray(final Collection<String> collection) {
|
||||
if (collection == null) {
|
||||
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
return toNoNullStringArray(collection.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new array of Strings without null elements. Internal method used to normalize exclude lists
|
||||
* (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException}
|
||||
* if an array element is {@code null}.
|
||||
*
|
||||
* @param array
|
||||
* The array to check
|
||||
* @return The given array or a new array without null.
|
||||
*/
|
||||
static String[] toNoNullStringArray(final Object[] array) {
|
||||
final List<String> list = new ArrayList<>(array.length);
|
||||
for (final Object e : array) {
|
||||
if (e != null) {
|
||||
list.add(e.toString());
|
||||
}
|
||||
}
|
||||
return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a String for a toString method excluding the given field names.
|
||||
*
|
||||
* @param object
|
||||
* The object to "toString".
|
||||
* @param excludeFieldNames
|
||||
* The field names to exclude
|
||||
* @return The toString value.
|
||||
*/
|
||||
public static String toStringExclude(final Object object, final String... excludeFieldNames) {
|
||||
return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString();
|
||||
}
|
||||
|
||||
private static Object checkNotNull(final Object obj) {
|
||||
return Validate.notNull(obj, "obj");
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to append static fields.
|
||||
*/
|
||||
private boolean appendStatics;
|
||||
|
||||
/**
|
||||
* Whether or not to append transient fields.
|
||||
*/
|
||||
private boolean appendTransients;
|
||||
|
||||
/**
|
||||
* Whether or not to append fields that are null.
|
||||
*/
|
||||
private boolean excludeNullValues;
|
||||
|
||||
/**
|
||||
* Which field names to exclude from output. Intended for fields like {@code "password"}.
|
||||
*
|
||||
* @since 3.0 this is protected instead of private
|
||||
*/
|
||||
protected String[] excludeFieldNames;
|
||||
|
||||
/**
|
||||
* The last super class to stop appending fields for.
|
||||
*/
|
||||
private Class<?> upToClass;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This constructor outputs using the default style set with {@code setDefaultStyle}.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to build a {@code toString} for, must not be {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object passed in is {@code null}
|
||||
*/
|
||||
public ReflectionToStringBuilder(final Object object) {
|
||||
super(checkNotNull(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default style is used.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to build a {@code toString} for, must not be {@code null}
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object passed in is {@code null}
|
||||
*/
|
||||
public ReflectionToStringBuilder(final Object object, final ToStringStyle style) {
|
||||
super(checkNotNull(object), style);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the style is {@code null}, the default style is used.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the buffer is {@code null}, a new one is created.
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* the Object to build a {@code toString} for
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param buffer
|
||||
* the {@code StringBuffer} to populate, may be {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if the Object passed in is {@code null}
|
||||
*/
|
||||
public ReflectionToStringBuilder(final Object object, final ToStringStyle style, final StringBuffer buffer) {
|
||||
super(checkNotNull(object), style, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object
|
||||
* @param object
|
||||
* the Object to build a {@code toString} for
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param buffer
|
||||
* the {@code StringBuffer} to populate, may be {@code null}
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @since 2.1
|
||||
*/
|
||||
public <T> ReflectionToStringBuilder(
|
||||
final T object, final ToStringStyle style, final StringBuffer buffer,
|
||||
final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics) {
|
||||
super(checkNotNull(object), style, buffer);
|
||||
this.setUpToClass(reflectUpToClass);
|
||||
this.setAppendTransients(outputTransients);
|
||||
this.setAppendStatics(outputStatics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the object
|
||||
* @param object
|
||||
* the Object to build a {@code toString} for
|
||||
* @param style
|
||||
* the style of the {@code toString} to create, may be {@code null}
|
||||
* @param buffer
|
||||
* the {@code StringBuffer} to populate, may be {@code null}
|
||||
* @param reflectUpToClass
|
||||
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||
* @param outputTransients
|
||||
* whether to include transient fields
|
||||
* @param outputStatics
|
||||
* whether to include static fields
|
||||
* @param excludeNullValues
|
||||
* whether to exclude fields who value is null
|
||||
* @since 3.6
|
||||
*/
|
||||
public <T> ReflectionToStringBuilder(
|
||||
final T object, final ToStringStyle style, final StringBuffer buffer,
|
||||
final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics,
|
||||
final boolean excludeNullValues) {
|
||||
super(checkNotNull(object), style, buffer);
|
||||
this.setUpToClass(reflectUpToClass);
|
||||
this.setAppendTransients(outputTransients);
|
||||
this.setAppendStatics(outputStatics);
|
||||
this.setExcludeNullValues(excludeNullValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not to append the given {@code Field}.
|
||||
* <ul>
|
||||
* <li>Transient fields are appended only if {@link #isAppendTransients()} returns {@code true}.
|
||||
* <li>Static fields are appended only if {@link #isAppendStatics()} returns {@code true}.
|
||||
* <li>Inner class fields are not appended.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param field
|
||||
* The Field to test.
|
||||
* @return Whether or not to append the given {@code Field}.
|
||||
*/
|
||||
protected boolean accept(final Field field) {
|
||||
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
|
||||
// Reject field from inner class.
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) {
|
||||
// Reject transient fields.
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) {
|
||||
// Reject static fields.
|
||||
return false;
|
||||
}
|
||||
if (this.excludeFieldNames != null
|
||||
&& Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
|
||||
// Reject fields from the getExcludeFieldNames list.
|
||||
return false;
|
||||
}
|
||||
return !field.isAnnotationPresent(ToStringExclude.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Appends the fields and values defined by the given object of the given Class.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a cycle is detected as an object is "toString()'ed", such an object is rendered as if
|
||||
* {@code Object.toString()} had been called and not implemented by the object.
|
||||
* </p>
|
||||
*
|
||||
* @param clazz
|
||||
* The class of object parameter
|
||||
*/
|
||||
protected void appendFieldsIn(final Class<?> clazz) {
|
||||
if (clazz.isArray()) {
|
||||
this.reflectionAppendArray(this.getObject());
|
||||
return;
|
||||
}
|
||||
// The elements in the returned array are not sorted and are not in any particular order.
|
||||
final Field[] fields = ArraySorter.sort(clazz.getDeclaredFields(), Comparator.comparing(Field::getName));
|
||||
AccessibleObject.setAccessible(fields, true);
|
||||
for (final Field field : fields) {
|
||||
final String fieldName = field.getName();
|
||||
if (this.accept(field)) {
|
||||
try {
|
||||
// Warning: Field.get(Object) creates wrappers objects
|
||||
// for primitive types.
|
||||
final Object fieldValue = this.getValue(field);
|
||||
if (!excludeNullValues || fieldValue != null) {
|
||||
this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class));
|
||||
}
|
||||
} catch (final IllegalAccessException ex) {
|
||||
//this can't happen. Would get a Security exception
|
||||
// instead
|
||||
//throw a runtime exception in case the impossible
|
||||
// happens.
|
||||
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the excludeFieldNames.
|
||||
*/
|
||||
public String[] getExcludeFieldNames() {
|
||||
return this.excludeFieldNames.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the last super class to stop appending fields for.
|
||||
* </p>
|
||||
*
|
||||
* @return The last super class to stop appending fields for.
|
||||
*/
|
||||
public Class<?> getUpToClass() {
|
||||
return this.upToClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Calls {@code java.lang.reflect.Field.get(Object)}.
|
||||
* </p>
|
||||
*
|
||||
* @param field
|
||||
* The Field to query.
|
||||
* @return The Object from the given Field.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* see {@link java.lang.reflect.Field#get(Object)}
|
||||
* @throws IllegalAccessException
|
||||
* see {@link java.lang.reflect.Field#get(Object)}
|
||||
*
|
||||
* @see java.lang.reflect.Field#get(Object)
|
||||
*/
|
||||
protected Object getValue(final Field field) throws IllegalAccessException {
|
||||
return field.get(this.getObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets whether or not to append static fields.
|
||||
* </p>
|
||||
*
|
||||
* @return Whether or not to append static fields.
|
||||
* @since 2.1
|
||||
*/
|
||||
public boolean isAppendStatics() {
|
||||
return this.appendStatics;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets whether or not to append transient fields.
|
||||
* </p>
|
||||
*
|
||||
* @return Whether or not to append transient fields.
|
||||
*/
|
||||
public boolean isAppendTransients() {
|
||||
return this.appendTransients;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets whether or not to append fields whose values are null.
|
||||
* </p>
|
||||
*
|
||||
* @return Whether or not to append fields whose values are null.
|
||||
* @since 3.6
|
||||
*/
|
||||
public boolean isExcludeNullValues() {
|
||||
return this.excludeNullValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Append to the {@code toString} an {@code Object} array.
|
||||
* </p>
|
||||
*
|
||||
* @param array
|
||||
* the array to add to the {@code toString}
|
||||
* @return this
|
||||
*/
|
||||
public ReflectionToStringBuilder reflectionAppendArray(final Object array) {
|
||||
this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether or not to append static fields.
|
||||
* </p>
|
||||
*
|
||||
* @param appendStatics
|
||||
* Whether or not to append static fields.
|
||||
* @since 2.1
|
||||
*/
|
||||
public void setAppendStatics(final boolean appendStatics) {
|
||||
this.appendStatics = appendStatics;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether or not to append transient fields.
|
||||
* </p>
|
||||
*
|
||||
* @param appendTransients
|
||||
* Whether or not to append transient fields.
|
||||
*/
|
||||
public void setAppendTransients(final boolean appendTransients) {
|
||||
this.appendTransients = appendTransients;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether or not to append fields whose values are null.
|
||||
* </p>
|
||||
*
|
||||
* @param excludeNullValues
|
||||
* Whether or not to append fields whose values are null.
|
||||
* @since 3.6
|
||||
*/
|
||||
public void setExcludeNullValues(final boolean excludeNullValues) {
|
||||
this.excludeNullValues = excludeNullValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field names to exclude.
|
||||
*
|
||||
* @param excludeFieldNamesParam
|
||||
* The excludeFieldNames to excluding from toString or {@code null}.
|
||||
* @return {@code this}
|
||||
*/
|
||||
public ReflectionToStringBuilder setExcludeFieldNames(final String... excludeFieldNamesParam) {
|
||||
if (excludeFieldNamesParam == null) {
|
||||
this.excludeFieldNames = null;
|
||||
} else {
|
||||
//clone and remove nulls
|
||||
this.excludeFieldNames = ArraySorter.sort(toNoNullStringArray(excludeFieldNamesParam));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the last super class to stop appending fields for.
|
||||
* </p>
|
||||
*
|
||||
* @param clazz
|
||||
* The last super class to stop appending fields for.
|
||||
*/
|
||||
public void setUpToClass(final Class<?> clazz) {
|
||||
if (clazz != null) {
|
||||
final Object object = getObject();
|
||||
if (object != null && !clazz.isInstance(object)) {
|
||||
throw new IllegalArgumentException("Specified class is not a superclass of the object");
|
||||
}
|
||||
}
|
||||
this.upToClass = clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the String built by this builder.
|
||||
* </p>
|
||||
*
|
||||
* @return the built string
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.getObject() == null) {
|
||||
return this.getStyle().getNullText();
|
||||
}
|
||||
Class<?> clazz = this.getObject().getClass();
|
||||
this.appendFieldsIn(clazz);
|
||||
while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
|
||||
clazz = clazz.getSuperclass();
|
||||
this.appendFieldsIn(clazz);
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
/**
|
||||
* <p>Works with {@link ToStringBuilder} to create a {@code toString}.</p>
|
||||
*
|
||||
* <p>This class is intended to be used as a singleton.
|
||||
* There is no need to instantiate a new style each time.
|
||||
* Simply instantiate the class once, customize the values as required, and
|
||||
* store the result in a public static final variable for the rest of the
|
||||
* program to access.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public class StandardToStringStyle extends ToStringStyle {
|
||||
|
||||
/**
|
||||
* Required for serialization support.
|
||||
*
|
||||
* @see java.io.Serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <p>Constructor.</p>
|
||||
*/
|
||||
public StandardToStringStyle() {
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether to use the class name.</p>
|
||||
*
|
||||
* @return the current useClassName flag
|
||||
*/
|
||||
@Override
|
||||
public boolean isUseClassName() { // NOPMD as this is implementing the abstract class
|
||||
return super.isUseClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether to use the class name.</p>
|
||||
*
|
||||
* @param useClassName the new useClassName flag
|
||||
*/
|
||||
@Override
|
||||
public void setUseClassName(final boolean useClassName) { // NOPMD as this is implementing the abstract class
|
||||
super.setUseClassName(useClassName);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether to output short or long class names.</p>
|
||||
*
|
||||
* @return the current useShortClassName flag
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class
|
||||
return super.isUseShortClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether to output short or long class names.</p>
|
||||
*
|
||||
* @param useShortClassName the new useShortClassName flag
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public void setUseShortClassName(final boolean useShortClassName) { // NOPMD as this is implementing the abstract class
|
||||
super.setUseShortClassName(useShortClassName);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether to use the identity hash code.</p>
|
||||
* @return the current useIdentityHashCode flag
|
||||
*/
|
||||
@Override
|
||||
public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class
|
||||
return super.isUseIdentityHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether to use the identity hash code.</p>
|
||||
*
|
||||
* @param useIdentityHashCode the new useIdentityHashCode flag
|
||||
*/
|
||||
@Override
|
||||
public void setUseIdentityHashCode(final boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class
|
||||
super.setUseIdentityHashCode(useIdentityHashCode);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether to use the field names passed in.</p>
|
||||
*
|
||||
* @return the current useFieldNames flag
|
||||
*/
|
||||
@Override
|
||||
public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class
|
||||
return super.isUseFieldNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether to use the field names passed in.</p>
|
||||
*
|
||||
* @param useFieldNames the new useFieldNames flag
|
||||
*/
|
||||
@Override
|
||||
public void setUseFieldNames(final boolean useFieldNames) { // NOPMD as this is implementing the abstract class
|
||||
super.setUseFieldNames(useFieldNames);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether to use full detail when the caller doesn't
|
||||
* specify.</p>
|
||||
*
|
||||
* @return the current defaultFullDetail flag
|
||||
*/
|
||||
@Override
|
||||
public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class
|
||||
return super.isDefaultFullDetail();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether to use full detail when the caller doesn't
|
||||
* specify.</p>
|
||||
*
|
||||
* @param defaultFullDetail the new defaultFullDetail flag
|
||||
*/
|
||||
@Override
|
||||
public void setDefaultFullDetail(final boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class
|
||||
super.setDefaultFullDetail(defaultFullDetail);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether to output array content detail.</p>
|
||||
*
|
||||
* @return the current array content detail setting
|
||||
*/
|
||||
@Override
|
||||
public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class
|
||||
return super.isArrayContentDetail();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether to output array content detail.</p>
|
||||
*
|
||||
* @param arrayContentDetail the new arrayContentDetail flag
|
||||
*/
|
||||
@Override
|
||||
public void setArrayContentDetail(final boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class
|
||||
super.setArrayContentDetail(arrayContentDetail);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the array start text.</p>
|
||||
*
|
||||
* @return the current array start text
|
||||
*/
|
||||
@Override
|
||||
public String getArrayStart() { // NOPMD as this is implementing the abstract class
|
||||
return super.getArrayStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the array start text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param arrayStart the new array start text
|
||||
*/
|
||||
@Override
|
||||
public void setArrayStart(final String arrayStart) { // NOPMD as this is implementing the abstract class
|
||||
super.setArrayStart(arrayStart);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the array end text.</p>
|
||||
*
|
||||
* @return the current array end text
|
||||
*/
|
||||
@Override
|
||||
public String getArrayEnd() { // NOPMD as this is implementing the abstract class
|
||||
return super.getArrayEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the array end text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param arrayEnd the new array end text
|
||||
*/
|
||||
@Override
|
||||
public void setArrayEnd(final String arrayEnd) { // NOPMD as this is implementing the abstract class
|
||||
super.setArrayEnd(arrayEnd);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the array separator text.</p>
|
||||
*
|
||||
* @return the current array separator text
|
||||
*/
|
||||
@Override
|
||||
public String getArraySeparator() { // NOPMD as this is implementing the abstract class
|
||||
return super.getArraySeparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the array separator text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param arraySeparator the new array separator text
|
||||
*/
|
||||
@Override
|
||||
public void setArraySeparator(final String arraySeparator) { // NOPMD as this is implementing the abstract class
|
||||
super.setArraySeparator(arraySeparator);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the content start text.</p>
|
||||
*
|
||||
* @return the current content start text
|
||||
*/
|
||||
@Override
|
||||
public String getContentStart() { // NOPMD as this is implementing the abstract class
|
||||
return super.getContentStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the content start text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param contentStart the new content start text
|
||||
*/
|
||||
@Override
|
||||
public void setContentStart(final String contentStart) { // NOPMD as this is implementing the abstract class
|
||||
super.setContentStart(contentStart);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the content end text.</p>
|
||||
*
|
||||
* @return the current content end text
|
||||
*/
|
||||
@Override
|
||||
public String getContentEnd() { // NOPMD as this is implementing the abstract class
|
||||
return super.getContentEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the content end text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param contentEnd the new content end text
|
||||
*/
|
||||
@Override
|
||||
public void setContentEnd(final String contentEnd) { // NOPMD as this is implementing the abstract class
|
||||
super.setContentEnd(contentEnd);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the field name value separator text.</p>
|
||||
*
|
||||
* @return the current field name value separator text
|
||||
*/
|
||||
@Override
|
||||
public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class
|
||||
return super.getFieldNameValueSeparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the field name value separator text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param fieldNameValueSeparator the new field name value separator text
|
||||
*/
|
||||
@Override
|
||||
public void setFieldNameValueSeparator(final String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class
|
||||
super.setFieldNameValueSeparator(fieldNameValueSeparator);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the field separator text.</p>
|
||||
*
|
||||
* @return the current field separator text
|
||||
*/
|
||||
@Override
|
||||
public String getFieldSeparator() { // NOPMD as this is implementing the abstract class
|
||||
return super.getFieldSeparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the field separator text.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param fieldSeparator the new field separator text
|
||||
*/
|
||||
@Override
|
||||
public void setFieldSeparator(final String fieldSeparator) { // NOPMD as this is implementing the abstract class
|
||||
super.setFieldSeparator(fieldSeparator);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether the field separator should be added at the start
|
||||
* of each buffer.</p>
|
||||
*
|
||||
* @return the fieldSeparatorAtStart flag
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class
|
||||
return super.isFieldSeparatorAtStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether the field separator should be added at the start
|
||||
* of each buffer.</p>
|
||||
*
|
||||
* @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class
|
||||
super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets whether the field separator should be added at the end
|
||||
* of each buffer.</p>
|
||||
*
|
||||
* @return fieldSeparatorAtEnd flag
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class
|
||||
return super.isFieldSeparatorAtEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether the field separator should be added at the end
|
||||
* of each buffer.</p>
|
||||
*
|
||||
* @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
|
||||
* @since 2.0
|
||||
*/
|
||||
@Override
|
||||
public void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class
|
||||
super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the text to output when {@code null} found.</p>
|
||||
*
|
||||
* @return the current text to output when {@code null} found
|
||||
*/
|
||||
@Override
|
||||
public String getNullText() { // NOPMD as this is implementing the abstract class
|
||||
return super.getNullText();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the text to output when {@code null} found.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param nullText the new text to output when {@code null} found
|
||||
*/
|
||||
@Override
|
||||
public void setNullText(final String nullText) { // NOPMD as this is implementing the abstract class
|
||||
super.setNullText(nullText);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the text to output when a {@code Collection},
|
||||
* {@code Map} or {@code Array} size is output.</p>
|
||||
*
|
||||
* <p>This is output before the size value.</p>
|
||||
*
|
||||
* @return the current start of size text
|
||||
*/
|
||||
@Override
|
||||
public String getSizeStartText() { // NOPMD as this is implementing the abstract class
|
||||
return super.getSizeStartText();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the start text to output when a {@code Collection},
|
||||
* {@code Map} or {@code Array} size is output.</p>
|
||||
*
|
||||
* <p>This is output before the size value.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted to
|
||||
* an empty String.</p>
|
||||
*
|
||||
* @param sizeStartText the new start of size text
|
||||
*/
|
||||
@Override
|
||||
public void setSizeStartText(final String sizeStartText) { // NOPMD as this is implementing the abstract class
|
||||
super.setSizeStartText(sizeStartText);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the end text to output when a {@code Collection},
|
||||
* {@code Map} or {@code Array} size is output.</p>
|
||||
*
|
||||
* <p>This is output after the size value.</p>
|
||||
*
|
||||
* @return the current end of size text
|
||||
*/
|
||||
@Override
|
||||
public String getSizeEndText() { // NOPMD as this is implementing the abstract class
|
||||
return super.getSizeEndText();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the end text to output when a {@code Collection},
|
||||
* {@code Map} or {@code Array} size is output.</p>
|
||||
*
|
||||
* <p>This is output after the size value.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted
|
||||
* to an empty String.</p>
|
||||
*
|
||||
* @param sizeEndText the new end of size text
|
||||
*/
|
||||
@Override
|
||||
public void setSizeEndText(final String sizeEndText) { // NOPMD as this is implementing the abstract class
|
||||
super.setSizeEndText(sizeEndText);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the start text to output when an {@code Object} is
|
||||
* output in summary mode.</p>
|
||||
*
|
||||
* <P>This is output before the size value.</p>
|
||||
*
|
||||
* @return the current start of summary text
|
||||
*/
|
||||
@Override
|
||||
public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class
|
||||
return super.getSummaryObjectStartText();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the start text to output when an {@code Object} is
|
||||
* output in summary mode.</p>
|
||||
*
|
||||
* <p>This is output before the size value.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted to
|
||||
* an empty String.</p>
|
||||
*
|
||||
* @param summaryObjectStartText the new start of summary text
|
||||
*/
|
||||
@Override
|
||||
public void setSummaryObjectStartText(final String summaryObjectStartText) { // NOPMD as this is implementing the abstract class
|
||||
super.setSummaryObjectStartText(summaryObjectStartText);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Gets the end text to output when an {@code Object} is
|
||||
* output in summary mode.</p>
|
||||
*
|
||||
* <p>This is output after the size value.</p>
|
||||
*
|
||||
* @return the current end of summary text
|
||||
*/
|
||||
@Override
|
||||
public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class
|
||||
return super.getSummaryObjectEndText();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the end text to output when an {@code Object} is
|
||||
* output in summary mode.</p>
|
||||
*
|
||||
* <p>This is output after the size value.</p>
|
||||
*
|
||||
* <p>{@code null} is accepted, but will be converted to
|
||||
* an empty String.</p>
|
||||
*
|
||||
* @param summaryObjectEndText the new end of summary text
|
||||
*/
|
||||
@Override
|
||||
public void setSummaryObjectEndText(final String summaryObjectEndText) { // NOPMD as this is implementing the abstract class
|
||||
super.setSummaryObjectEndText(summaryObjectEndText);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
35
before/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java
Executable file
35
before/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java
Executable file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Use this annotation to exclude a field from being used by
|
||||
* the {@link ReflectionToStringBuilder}.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ToStringExclude {
|
||||
// empty
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.apache.commons.lang3.builder;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Use this annotation on the fields to get the summary instead of the detailed
|
||||
* information when using {@link ReflectionToStringBuilder}.
|
||||
*
|
||||
* <p>
|
||||
* Notice that not all {@link ToStringStyle} implementations support the
|
||||
* appendSummary method.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ToStringSummary {
|
||||
// empty
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* <p>Assists in creating consistent {@code equals(Object)}, {@code toString()}, {@code hashCode()}, and {@code compareTo(Object)} methods.
|
||||
* These classes are not thread-safe.</p>
|
||||
*
|
||||
* <p>When you write a {@link java.lang.Object#hashCode() hashCode()}, do you check Bloch's Effective Java? No?
|
||||
* You just hack in a quick number?
|
||||
* Well {@link org.apache.commons.lang3.builder.HashCodeBuilder} will save your day.
|
||||
* It, and its buddies ({@link org.apache.commons.lang3.builder.EqualsBuilder}, {@link org.apache.commons.lang3.builder.CompareToBuilder}, {@link org.apache.commons.lang3.builder.ToStringBuilder}), take care of the nasty bits while you focus on the important bits, like which fields will go into making up the hashcode.</p>
|
||||
*
|
||||
* @see java.lang.Object#equals(Object)
|
||||
* @see java.lang.Object#toString()
|
||||
* @see java.lang.Object#hashCode()
|
||||
* @see java.lang.Comparable#compareTo(Object)
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
package org.apache.commons.lang3.builder;
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.compare;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* <p>Utility library to provide helper methods for translating {@link Comparable#compareTo} result into a boolean.</p>
|
||||
*
|
||||
* <p>Example: {@code boolean x = is(myComparable).lessThanOrEqualTo(otherComparable)}</p>
|
||||
*
|
||||
* <p>#ThreadSafe#</p>
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
public class ComparableUtils {
|
||||
|
||||
/**
|
||||
* Provides access to the available methods
|
||||
*
|
||||
* @param <A> the type of objects that this object may be compared against.
|
||||
*/
|
||||
public static class ComparableCheckBuilder<A extends Comparable<A>> {
|
||||
|
||||
private final A a;
|
||||
|
||||
private ComparableCheckBuilder(final A a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} is object passed to {@link #is}.
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @param c the object to compare to the base object
|
||||
* @return true if the base object is between b and c
|
||||
*/
|
||||
public boolean between(final A b, final A c) {
|
||||
return betweenOrdered(b, c) || betweenOrdered(c, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is object passed to {@link #is}.
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @param c the object to compare to the base object
|
||||
* @return true if the base object is between b and c and not equal to those
|
||||
*/
|
||||
public boolean betweenExclusive(final A b, final A c) {
|
||||
return betweenOrderedExclusive(b, c) || betweenOrderedExclusive(c, b);
|
||||
}
|
||||
|
||||
private boolean betweenOrdered(final A b, final A c) {
|
||||
return greaterThanOrEqualTo(b) && lessThanOrEqualTo(c);
|
||||
}
|
||||
|
||||
private boolean betweenOrderedExclusive(final A b, final A c) {
|
||||
return greaterThan(b) && lessThan(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object passed to {@link #is} is equal to {@code b}
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @return true if the value returned by {@link Comparable#compareTo} is equal to {@code 0}
|
||||
*/
|
||||
public boolean equalTo(final A b) {
|
||||
return a.compareTo(b) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object passed to {@link #is} is greater than {@code b}
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @return true if the value returned by {@link Comparable#compareTo} is greater than {@code 0}
|
||||
*/
|
||||
public boolean greaterThan(final A b) {
|
||||
return a.compareTo(b) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object passed to {@link #is} is greater than or equal to {@code b}
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @return true if the value returned by {@link Comparable#compareTo} is greater than or equal to {@code 0}
|
||||
*/
|
||||
public boolean greaterThanOrEqualTo(final A b) {
|
||||
return a.compareTo(b) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object passed to {@link #is} is less than {@code b}
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @return true if the value returned by {@link Comparable#compareTo} is less than {@code 0}
|
||||
*/
|
||||
public boolean lessThan(final A b) {
|
||||
return a.compareTo(b) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object passed to {@link #is} is less than or equal to {@code b}
|
||||
*
|
||||
* @param b the object to compare to the base object
|
||||
* @return true if the value returned by {@link Comparable#compareTo} is less than or equal to {@code 0}
|
||||
*/
|
||||
public boolean lessThanOrEqualTo(final A b) {
|
||||
return a.compareTo(b) <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} is the tested object.
|
||||
*
|
||||
* @param b the object to compare to the tested object
|
||||
* @param c the object to compare to the tested object
|
||||
* @param <A> type of the test object
|
||||
* @return a predicate for true if the tested object is between b and c
|
||||
*/
|
||||
public static <A extends Comparable<A>> Predicate<A> between(final A b, final A c) {
|
||||
return a -> is(a).between(b, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is the tested object.
|
||||
*
|
||||
* @param b the object to compare to the tested object
|
||||
* @param c the object to compare to the tested object
|
||||
* @param <A> type of the test object
|
||||
* @return a predicate for true if the tested object is between b and c and not equal to those
|
||||
*/
|
||||
public static <A extends Comparable<A>> Predicate<A> betweenExclusive(final A b, final A c) {
|
||||
return a -> is(a).betweenExclusive(b, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the tested object is greater than or equal to {@code b}
|
||||
*
|
||||
* @param b the object to compare to the tested object
|
||||
* @param <A> type of the test object
|
||||
* @return a predicate for true if the value returned by {@link Comparable#compareTo}
|
||||
* is greater than or equal to {@code 0}
|
||||
*/
|
||||
public static <A extends Comparable<A>> Predicate<A> ge(final A b) {
|
||||
return a -> is(a).greaterThanOrEqualTo(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the tested object is greater than {@code b}
|
||||
*
|
||||
* @param b the object to compare to the tested object
|
||||
* @param <A> type of the test object
|
||||
* @return a predicate for true if the value returned by {@link Comparable#compareTo} is greater than {@code 0}
|
||||
*/
|
||||
public static <A extends Comparable<A>> Predicate<A> gt(final A b) {
|
||||
return a -> is(a).greaterThan(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the available methods
|
||||
*
|
||||
* @param a base object in the further comparison
|
||||
* @param <A> type of the base object
|
||||
* @return a builder object with further methods
|
||||
*/
|
||||
public static <A extends Comparable<A>> ComparableCheckBuilder<A> is(final A a) {
|
||||
return new ComparableCheckBuilder<>(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the tested object is less than or equal to {@code b}
|
||||
*
|
||||
* @param b the object to compare to the tested object
|
||||
* @param <A> type of the test object
|
||||
* @return a predicate for true if the value returned by {@link Comparable#compareTo}
|
||||
* is less than or equal to {@code 0}
|
||||
*/
|
||||
public static <A extends Comparable<A>> Predicate<A> le(final A b) {
|
||||
return a -> is(a).lessThanOrEqualTo(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the tested object is less than {@code b}
|
||||
*
|
||||
* @param b the object to compare to the tested object
|
||||
* @param <A> type of the test object
|
||||
* @return a predicate for true if the value returned by {@link Comparable#compareTo} is less than {@code 0}
|
||||
*/
|
||||
public static <A extends Comparable<A>> Predicate<A> lt(final A b) {
|
||||
return a -> is(a).lessThan(b);
|
||||
}
|
||||
|
||||
private ComparableUtils() {}
|
||||
}
|
|
@ -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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.compare;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Compares Object's {@link Object#toString()} values.
|
||||
*
|
||||
* This class is stateless.
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
public final class ObjectToStringComparator implements Comparator<Object>, Serializable {
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
*
|
||||
* This class is stateless.
|
||||
*/
|
||||
public static final ObjectToStringComparator INSTANCE = new ObjectToStringComparator();
|
||||
|
||||
/**
|
||||
* For {@link Serializable}.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public int compare(final Object o1, final Object o2) {
|
||||
if (o1 == null && o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (o1 == null) {
|
||||
return 1;
|
||||
}
|
||||
if (o2 == null) {
|
||||
return -1;
|
||||
}
|
||||
final String string1 = o1.toString();
|
||||
final String string2 = o2.toString();
|
||||
// No guarantee that toString() returns a non-null value, despite what Spotbugs thinks.
|
||||
if (string1 == null && string2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (string1 == null) {
|
||||
return 1;
|
||||
}
|
||||
if (string2 == null) {
|
||||
return -1;
|
||||
}
|
||||
return string1.compareTo(string2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides classes to work with the {@link java.lang.Comparable} and {@link java.util.Comparator} interfaces.
|
||||
*
|
||||
* @since 3.10
|
||||
*/
|
||||
package org.apache.commons.lang3.compare;
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Base class for circuit breakers.
|
||||
*
|
||||
* @param <T> the type of the value monitored by this circuit breaker
|
||||
* @since 3.5
|
||||
*/
|
||||
public abstract class AbstractCircuitBreaker<T> implements CircuitBreaker<T> {
|
||||
|
||||
/**
|
||||
* The name of the <em>open</em> property as it is passed to registered
|
||||
* change listeners.
|
||||
*/
|
||||
public static final String PROPERTY_NAME = "open";
|
||||
|
||||
/** The current state of this circuit breaker. */
|
||||
protected final AtomicReference<State> state = new AtomicReference<>(State.CLOSED);
|
||||
|
||||
/** An object for managing change listeners registered at this instance. */
|
||||
private final PropertyChangeSupport changeSupport;
|
||||
|
||||
/**
|
||||
* Creates an {@code AbstractCircuitBreaker}. It also creates an internal {@code PropertyChangeSupport}.
|
||||
*/
|
||||
public AbstractCircuitBreaker() {
|
||||
changeSupport = new PropertyChangeSupport(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return isOpen(state.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return !isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean checkState();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean incrementAndCheckState(T increment);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
changeState(State.CLOSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void open() {
|
||||
changeState(State.OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given state value to a boolean <em>open</em> property.
|
||||
*
|
||||
* @param state the state to be converted
|
||||
* @return the boolean open flag
|
||||
*/
|
||||
protected static boolean isOpen(final State state) {
|
||||
return state == State.OPEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the internal state of this circuit breaker. If there is actually a change
|
||||
* of the state value, all registered change listeners are notified.
|
||||
*
|
||||
* @param newState the new state to be set
|
||||
*/
|
||||
protected void changeState(final State newState) {
|
||||
if (state.compareAndSet(newState.oppositeState(), newState)) {
|
||||
changeSupport.firePropertyChange(PROPERTY_NAME, !isOpen(newState), isOpen(newState));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a change listener to this circuit breaker. This listener is notified whenever
|
||||
* the state of this circuit breaker changes. If the listener is
|
||||
* <strong>null</strong>, it is silently ignored.
|
||||
*
|
||||
* @param listener the listener to be added
|
||||
*/
|
||||
public void addChangeListener(final PropertyChangeListener listener) {
|
||||
changeSupport.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified change listener from this circuit breaker.
|
||||
*
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeChangeListener(final PropertyChangeListener listener) {
|
||||
changeSupport.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal enumeration representing the different states of a circuit
|
||||
* breaker. This class also contains some logic for performing state
|
||||
* transitions. This is done to avoid complex if-conditions in the code of
|
||||
* {@code CircuitBreaker}.
|
||||
*/
|
||||
protected enum State {
|
||||
|
||||
/** The closed state. */
|
||||
CLOSED {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public State oppositeState() {
|
||||
return OPEN;
|
||||
}
|
||||
},
|
||||
|
||||
/** The open state. */
|
||||
OPEN {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public State oppositeState() {
|
||||
return CLOSED;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the opposite state to the represented state. This is useful
|
||||
* for flipping the current state.
|
||||
*
|
||||
* @return the opposite state
|
||||
*/
|
||||
public abstract State oppositeState();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A specialized implementation of the {@code ConcurrentInitializer} interface
|
||||
* based on an {@link AtomicReference} variable.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class maintains a member field of type {@code AtomicReference}. It
|
||||
* implements the following algorithm to create and initialize an object in its
|
||||
* {@link #get()} method:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>First it is checked whether the {@code AtomicReference} variable contains
|
||||
* already a value. If this is the case, the value is directly returned.</li>
|
||||
* <li>Otherwise the {@link #initialize()} method is called. This method must be
|
||||
* defined in concrete subclasses to actually create the managed object.</li>
|
||||
* <li>After the object was created by {@link #initialize()} it is checked
|
||||
* whether the {@code AtomicReference} variable is still undefined. This has to
|
||||
* be done because in the meantime another thread may have initialized the
|
||||
* object. If the reference is still empty, the newly created object is stored
|
||||
* in it and returned by this method.</li>
|
||||
* <li>Otherwise the value stored in the {@code AtomicReference} is returned.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Because atomic variables are used this class does not need any
|
||||
* synchronization. So there is no danger of deadlock, and access to the managed
|
||||
* object is efficient. However, if multiple threads access the {@code
|
||||
* AtomicInitializer} object before it has been initialized almost at the same
|
||||
* time, it can happen that {@link #initialize()} is called multiple times. The
|
||||
* algorithm outlined above guarantees that {@link #get()} always returns the
|
||||
* same object though.
|
||||
* </p>
|
||||
* <p>
|
||||
* Compared with the {@link LazyInitializer} class, this class can be more
|
||||
* efficient because it does not need synchronization. The drawback is that the
|
||||
* {@link #initialize()} method can be called multiple times which may be
|
||||
* problematic if the creation of the managed object is expensive. As a rule of
|
||||
* thumb this initializer implementation is preferable if there are not too many
|
||||
* threads involved and the probability that multiple threads access an
|
||||
* uninitialized object is small. If there is high parallelism,
|
||||
* {@link LazyInitializer} is more appropriate.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public abstract class AtomicInitializer<T> implements ConcurrentInitializer<T> {
|
||||
/** Holds the reference to the managed object. */
|
||||
private final AtomicReference<T> reference = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Returns the object managed by this initializer. The object is created if
|
||||
* it is not available yet and stored internally. This method always returns
|
||||
* the same object.
|
||||
*
|
||||
* @return the object created by this {@code AtomicInitializer}
|
||||
* @throws ConcurrentException if an error occurred during initialization of
|
||||
* the object
|
||||
*/
|
||||
@Override
|
||||
public T get() throws ConcurrentException {
|
||||
T result = reference.get();
|
||||
|
||||
if (result == null) {
|
||||
result = initialize();
|
||||
if (!reference.compareAndSet(null, result)) {
|
||||
// another thread has initialized the reference
|
||||
result = reference.get();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the object managed by this {@code
|
||||
* AtomicInitializer}. This method is called by {@link #get()} when the
|
||||
* managed object is not available yet. An implementation can focus on the
|
||||
* creation of the object. No synchronization is needed, as this is already
|
||||
* handled by {@code get()}. As stated by the class comment, it is possible
|
||||
* that this method is called multiple times.
|
||||
*
|
||||
* @return the managed data object
|
||||
* @throws ConcurrentException if an error occurs during object creation
|
||||
*/
|
||||
protected abstract T initialize() throws ConcurrentException;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A specialized {@code ConcurrentInitializer} implementation which is similar
|
||||
* to {@link AtomicInitializer}, but ensures that the {@link #initialize()}
|
||||
* method is called only once.
|
||||
* </p>
|
||||
* <p>
|
||||
* As {@link AtomicInitializer} this class is based on atomic variables, so it
|
||||
* can create an object under concurrent access without synchronization.
|
||||
* However, it implements an additional check to guarantee that the
|
||||
* {@link #initialize()} method which actually creates the object cannot be
|
||||
* called multiple times.
|
||||
* </p>
|
||||
* <p>
|
||||
* Because of this additional check this implementation is slightly less
|
||||
* efficient than {@link AtomicInitializer}, but if the object creation in the
|
||||
* {@code initialize()} method is expensive or if multiple invocations of
|
||||
* {@code initialize()} are problematic, it is the better alternative.
|
||||
* </p>
|
||||
* <p>
|
||||
* From its semantics this class has the same properties as
|
||||
* {@link LazyInitializer}. It is a "save" implementation of the lazy
|
||||
* initializer pattern. Comparing both classes in terms of efficiency is
|
||||
* difficult because which one is faster depends on multiple factors. Because
|
||||
* {@code AtomicSafeInitializer} does not use synchronization at all it probably
|
||||
* outruns {@link LazyInitializer}, at least under low or moderate concurrent
|
||||
* access. Developers should run their own benchmarks on the expected target
|
||||
* platform to decide which implementation is suitable for their specific use
|
||||
* case.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public abstract class AtomicSafeInitializer<T> implements
|
||||
ConcurrentInitializer<T> {
|
||||
/** A guard which ensures that initialize() is called only once. */
|
||||
private final AtomicReference<AtomicSafeInitializer<T>> factory =
|
||||
new AtomicReference<>();
|
||||
|
||||
/** Holds the reference to the managed object. */
|
||||
private final AtomicReference<T> reference = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Gets (and initialize, if not initialized yet) the required object
|
||||
*
|
||||
* @return lazily initialized object
|
||||
* @throws ConcurrentException if the initialization of the object causes an
|
||||
* exception
|
||||
*/
|
||||
@Override
|
||||
public final T get() throws ConcurrentException {
|
||||
T result;
|
||||
|
||||
while ((result = reference.get()) == null) {
|
||||
if (factory.compareAndSet(null, this)) {
|
||||
reference.set(initialize());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the object managed by this
|
||||
* {@code AtomicInitializer}. This method is called by {@link #get()} when
|
||||
* the managed object is not available yet. An implementation can focus on
|
||||
* the creation of the object. No synchronization is needed, as this is
|
||||
* already handled by {@code get()}. This method is guaranteed to be called
|
||||
* only once.
|
||||
*
|
||||
* @return the managed data object
|
||||
* @throws ConcurrentException if an error occurs during object creation
|
||||
*/
|
||||
protected abstract T initialize() throws ConcurrentException;
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A class that allows complex initialization operations in a background task.
|
||||
* </p>
|
||||
* <p>
|
||||
* Applications often have to do some expensive initialization steps when they
|
||||
* are started, e.g. constructing a connection to a database, reading a
|
||||
* configuration file, etc. Doing these things in parallel can enhance
|
||||
* performance as the CPU load can be improved. However, when access to the
|
||||
* resources initialized in a background thread is actually required,
|
||||
* synchronization has to be performed to ensure that their initialization is
|
||||
* complete.
|
||||
* </p>
|
||||
* <p>
|
||||
* This abstract base class provides support for this use case. A concrete
|
||||
* subclass must implement the {@link #initialize()} method. Here an arbitrary
|
||||
* initialization can be implemented, and a result object can be returned. With
|
||||
* this method in place the basic usage of this class is as follows (where
|
||||
* {@code MyBackgroundInitializer} is a concrete subclass):
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* MyBackgroundInitializer initializer = new MyBackgroundInitializer();
|
||||
* initializer.start();
|
||||
* // Now do some other things. Initialization runs in a parallel thread
|
||||
* ...
|
||||
* // Wait for the end of initialization and access the result object
|
||||
* Object result = initializer.get();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* After the construction of a {@code BackgroundInitializer} object its
|
||||
* {@link #start()} method has to be called. This starts the background
|
||||
* processing. The application can now continue to do other things. When it
|
||||
* needs access to the object produced by the {@code BackgroundInitializer} it
|
||||
* calls its {@link #get()} method. If initialization is already complete,
|
||||
* {@link #get()} returns the result object immediately. Otherwise it blocks
|
||||
* until the result object is fully constructed.
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code BackgroundInitializer} is a thin wrapper around a {@code Future}
|
||||
* object and uses an {@code ExecutorService} for running the background
|
||||
* initialization task. It is possible to pass in an {@code ExecutorService} at
|
||||
* construction time or set one using {@code setExternalExecutor()} before
|
||||
* {@code start()} was called. Then this object is used to spawn the background
|
||||
* task. If no {@code ExecutorService} has been provided, {@code
|
||||
* BackgroundInitializer} creates a temporary {@code ExecutorService} and
|
||||
* destroys it when initialization is complete.
|
||||
* </p>
|
||||
* <p>
|
||||
* The methods provided by {@code BackgroundInitializer} provide for minimal
|
||||
* interaction with the wrapped {@code Future} object. It is also possible to
|
||||
* obtain the {@code Future} object directly. Then the enhanced functionality
|
||||
* offered by {@code Future} can be used, e.g. to check whether the background
|
||||
* operation is complete or to cancel the operation.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public abstract class BackgroundInitializer<T> implements
|
||||
ConcurrentInitializer<T> {
|
||||
/** The external executor service for executing tasks. */
|
||||
private ExecutorService externalExecutor; // @GuardedBy("this")
|
||||
|
||||
/** A reference to the executor service that is actually used. */
|
||||
private ExecutorService executor; // @GuardedBy("this")
|
||||
|
||||
/** Stores the handle to the background task. */
|
||||
private Future<T> future; // @GuardedBy("this")
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code BackgroundInitializer}. No external
|
||||
* {@code ExecutorService} is used.
|
||||
*/
|
||||
protected BackgroundInitializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code BackgroundInitializer} and initializes
|
||||
* it with the given {@code ExecutorService}. If the {@code ExecutorService}
|
||||
* is not null, the background task for initializing this object will be
|
||||
* scheduled at this service. Otherwise a new temporary {@code
|
||||
* ExecutorService} is created.
|
||||
*
|
||||
* @param exec an external {@code ExecutorService} to be used for task
|
||||
* execution
|
||||
*/
|
||||
protected BackgroundInitializer(final ExecutorService exec) {
|
||||
setExternalExecutor(exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the external {@code ExecutorService} to be used by this class.
|
||||
*
|
||||
* @return the {@code ExecutorService}
|
||||
*/
|
||||
public final synchronized ExecutorService getExternalExecutor() {
|
||||
return externalExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag whether this {@code BackgroundInitializer} has already
|
||||
* been started.
|
||||
*
|
||||
* @return a flag whether the {@link #start()} method has already been
|
||||
* called
|
||||
*/
|
||||
public synchronized boolean isStarted() {
|
||||
return future != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@code ExecutorService} to be used by this class. The {@code
|
||||
* ExecutorService} passed to this method is used for executing the
|
||||
* background task. Thus it is possible to re-use an already existing
|
||||
* {@code ExecutorService} or to use a specially configured one. If no
|
||||
* {@code ExecutorService} is set, this instance creates a temporary one and
|
||||
* destroys it after background initialization is complete. Note that this
|
||||
* method must be called before {@link #start()}; otherwise an exception is
|
||||
* thrown.
|
||||
*
|
||||
* @param externalExecutor the {@code ExecutorService} to be used
|
||||
* @throws IllegalStateException if this initializer has already been
|
||||
* started
|
||||
*/
|
||||
public final synchronized void setExternalExecutor(
|
||||
final ExecutorService externalExecutor) {
|
||||
if (isStarted()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot set ExecutorService after start()!");
|
||||
}
|
||||
|
||||
this.externalExecutor = externalExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the background initialization. With this method the initializer
|
||||
* becomes active and invokes the {@link #initialize()} method in a
|
||||
* background task. A {@code BackgroundInitializer} can be started exactly
|
||||
* once. The return value of this method determines whether the start was
|
||||
* successful: only the first invocation of this method returns <b>true</b>,
|
||||
* following invocations will return <b>false</b>.
|
||||
*
|
||||
* @return a flag whether the initializer could be started successfully
|
||||
*/
|
||||
public synchronized boolean start() {
|
||||
// Not yet started?
|
||||
if (!isStarted()) {
|
||||
|
||||
// Determine the executor to use and whether a temporary one has to
|
||||
// be created
|
||||
final ExecutorService tempExec;
|
||||
executor = getExternalExecutor();
|
||||
if (executor == null) {
|
||||
executor = tempExec = createExecutor();
|
||||
} else {
|
||||
tempExec = null;
|
||||
}
|
||||
|
||||
future = executor.submit(createTask(tempExec));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the background initialization. This method blocks
|
||||
* until initialization is complete. If the background processing caused a
|
||||
* runtime exception, it is directly thrown by this method. Checked
|
||||
* exceptions, including {@code InterruptedException} are wrapped in a
|
||||
* {@link ConcurrentException}. Calling this method before {@link #start()}
|
||||
* was called causes an {@code IllegalStateException} exception to be
|
||||
* thrown.
|
||||
*
|
||||
* @return the object produced by this initializer
|
||||
* @throws ConcurrentException if a checked exception occurred during
|
||||
* background processing
|
||||
* @throws IllegalStateException if {@link #start()} has not been called
|
||||
*/
|
||||
@Override
|
||||
public T get() throws ConcurrentException {
|
||||
try {
|
||||
return getFuture().get();
|
||||
} catch (final ExecutionException execex) {
|
||||
ConcurrentUtils.handleCause(execex);
|
||||
return null; // should not be reached
|
||||
} catch (final InterruptedException iex) {
|
||||
// reset interrupted state
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ConcurrentException(iex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Future} object that was created when {@link #start()}
|
||||
* was called. Therefore this method can only be called after {@code
|
||||
* start()}.
|
||||
*
|
||||
* @return the {@code Future} object wrapped by this initializer
|
||||
* @throws IllegalStateException if {@link #start()} has not been called
|
||||
*/
|
||||
public synchronized Future<T> getFuture() {
|
||||
if (future == null) {
|
||||
throw new IllegalStateException("start() must be called first!");
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code ExecutorService} that is actually used for executing
|
||||
* the background task. This method can be called after {@link #start()}
|
||||
* (before {@code start()} it returns <b>null</b>). If an external executor
|
||||
* was set, this is also the active executor. Otherwise this method returns
|
||||
* the temporary executor that was created by this object.
|
||||
*
|
||||
* @return the {@code ExecutorService} for executing the background task
|
||||
*/
|
||||
protected final synchronized ExecutorService getActiveExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of background tasks to be created for this
|
||||
* initializer. This information is evaluated when a temporary {@code
|
||||
* ExecutorService} is created. This base implementation returns 1. Derived
|
||||
* classes that do more complex background processing can override it. This
|
||||
* method is called from a synchronized block by the {@link #start()}
|
||||
* method. Therefore overriding methods should be careful with obtaining
|
||||
* other locks and return as fast as possible.
|
||||
*
|
||||
* @return the number of background tasks required by this initializer
|
||||
*/
|
||||
protected int getTaskCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the initialization. This method is called in a background task
|
||||
* when this {@code BackgroundInitializer} is started. It must be
|
||||
* implemented by a concrete subclass. An implementation is free to perform
|
||||
* arbitrary initialization. The object returned by this method can be
|
||||
* queried using the {@link #get()} method.
|
||||
*
|
||||
* @return a result object
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
protected abstract T initialize() throws Exception;
|
||||
|
||||
/**
|
||||
* Creates a task for the background initialization. The {@code Callable}
|
||||
* object returned by this method is passed to the {@code ExecutorService}.
|
||||
* This implementation returns a task that invokes the {@link #initialize()}
|
||||
* method. If a temporary {@code ExecutorService} is used, it is destroyed
|
||||
* at the end of the task.
|
||||
*
|
||||
* @param execDestroy the {@code ExecutorService} to be destroyed by the
|
||||
* task
|
||||
* @return a task for the background initialization
|
||||
*/
|
||||
private Callable<T> createTask(final ExecutorService execDestroy) {
|
||||
return new InitializationTask(execDestroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@code ExecutorService} to be used. This method is called if
|
||||
* no {@code ExecutorService} was provided at construction time.
|
||||
*
|
||||
* @return the {@code ExecutorService} to be used
|
||||
*/
|
||||
private ExecutorService createExecutor() {
|
||||
return Executors.newFixedThreadPool(getTaskCount());
|
||||
}
|
||||
|
||||
private class InitializationTask implements Callable<T> {
|
||||
/** Stores the executor service to be destroyed at the end. */
|
||||
private final ExecutorService execFinally;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code InitializationTask} and initializes
|
||||
* it with the {@code ExecutorService} to be destroyed at the end.
|
||||
*
|
||||
* @param exec the {@code ExecutorService}
|
||||
*/
|
||||
InitializationTask(final ExecutorService exec) {
|
||||
execFinally = exec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates initialization and returns the result.
|
||||
*
|
||||
* @return the result object
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
try {
|
||||
return initialize();
|
||||
} finally {
|
||||
if (execFinally != null) {
|
||||
execFinally.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An implementation of the {@code ThreadFactory} interface that provides some
|
||||
* configuration options for the threads it creates.
|
||||
* </p>
|
||||
* <p>
|
||||
* A {@code ThreadFactory} is used for instance by an {@code ExecutorService} to
|
||||
* create the threads it uses for executing tasks. In many cases users do not
|
||||
* have to care about a {@code ThreadFactory} because the default one used by an
|
||||
* {@code ExecutorService} will do. However, if there are special requirements
|
||||
* for the threads, a custom {@code ThreadFactory} has to be created.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class provides some frequently needed configuration options for the
|
||||
* threads it creates. These are the following:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>A name pattern for the threads created by this factory can be specified.
|
||||
* This is often useful if an application uses multiple executor services for
|
||||
* different purposes. If the names of the threads used by these services have
|
||||
* meaningful names, log output or exception traces can be much easier to read.
|
||||
* Naming patterns are <em>format strings</em> as used by the {@code
|
||||
* String.format()} method. The string can contain the place holder {@code %d}
|
||||
* which will be replaced by the number of the current thread ({@code
|
||||
* ThreadFactoryImpl} keeps a counter of the threads it has already created).
|
||||
* For instance, the naming pattern {@code "My %d. worker thread"} will result
|
||||
* in thread names like {@code "My 1. worker thread"}, {@code
|
||||
* "My 2. worker thread"} and so on.</li>
|
||||
* <li>A flag whether the threads created by this factory should be daemon
|
||||
* threads. This can impact the exit behavior of the current Java application
|
||||
* because the JVM shuts down if there are only daemon threads running.</li>
|
||||
* <li>The priority of the thread. Here an integer value can be provided. The
|
||||
* {@code java.lang.Thread} class defines constants for valid ranges of priority
|
||||
* values.</li>
|
||||
* <li>The {@code UncaughtExceptionHandler} for the thread. This handler is
|
||||
* called if an uncaught exception occurs within the thread.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* {@code BasicThreadFactory} wraps another thread factory which actually
|
||||
* creates new threads. The configuration options are set on the threads created
|
||||
* by the wrapped thread factory. On construction time the factory to be wrapped
|
||||
* can be specified. If none is provided, a default {@code ThreadFactory} is
|
||||
* used.
|
||||
* </p>
|
||||
* <p>
|
||||
* Instances of {@code BasicThreadFactory} are not created directly, but the
|
||||
* nested {@code Builder} class is used for this purpose. Using the builder only
|
||||
* the configuration options an application is interested in need to be set. The
|
||||
* following example shows how a {@code BasicThreadFactory} is created and
|
||||
* installed in an {@code ExecutorService}:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* // Create a factory that produces daemon threads with a naming pattern and
|
||||
* // a priority
|
||||
* BasicThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
* .namingPattern("workerthread-%d")
|
||||
* .daemon(true)
|
||||
* .priority(Thread.MAX_PRIORITY)
|
||||
* .build();
|
||||
* // Create an executor service for single-threaded execution
|
||||
* ExecutorService exec = Executors.newSingleThreadExecutor(factory);
|
||||
* </pre>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BasicThreadFactory implements ThreadFactory {
|
||||
/** A counter for the threads created by this factory. */
|
||||
private final AtomicLong threadCounter;
|
||||
|
||||
/** Stores the wrapped factory. */
|
||||
private final ThreadFactory wrappedFactory;
|
||||
|
||||
/** Stores the uncaught exception handler. */
|
||||
private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
||||
|
||||
/** Stores the naming pattern for newly created threads. */
|
||||
private final String namingPattern;
|
||||
|
||||
/** Stores the priority. */
|
||||
private final Integer priority;
|
||||
|
||||
/** Stores the daemon status flag. */
|
||||
private final Boolean daemon;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ThreadFactoryImpl} and configures it
|
||||
* from the specified {@code Builder} object.
|
||||
*
|
||||
* @param builder the {@code Builder} object
|
||||
*/
|
||||
private BasicThreadFactory(final Builder builder) {
|
||||
if (builder.wrappedFactory == null) {
|
||||
wrappedFactory = Executors.defaultThreadFactory();
|
||||
} else {
|
||||
wrappedFactory = builder.wrappedFactory;
|
||||
}
|
||||
|
||||
namingPattern = builder.namingPattern;
|
||||
priority = builder.priority;
|
||||
daemon = builder.daemon;
|
||||
uncaughtExceptionHandler = builder.exceptionHandler;
|
||||
|
||||
threadCounter = new AtomicLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapped {@code ThreadFactory}. This factory is used for
|
||||
* actually creating threads. This method never returns <b>null</b>. If no
|
||||
* {@code ThreadFactory} was passed when this object was created, a default
|
||||
* thread factory is returned.
|
||||
*
|
||||
* @return the wrapped {@code ThreadFactory}
|
||||
*/
|
||||
public final ThreadFactory getWrappedFactory() {
|
||||
return wrappedFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the naming pattern for naming newly created threads. Result can
|
||||
* be <b>null</b> if no naming pattern was provided.
|
||||
*
|
||||
* @return the naming pattern
|
||||
*/
|
||||
public final String getNamingPattern() {
|
||||
return namingPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the daemon flag. This flag determines whether newly created
|
||||
* threads should be daemon threads. If <b>true</b>, this factory object
|
||||
* calls {@code setDaemon(true)} on the newly created threads. Result can be
|
||||
* <b>null</b> if no daemon flag was provided at creation time.
|
||||
*
|
||||
* @return the daemon flag
|
||||
*/
|
||||
public final Boolean getDaemonFlag() {
|
||||
return daemon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of the threads created by this factory. Result can
|
||||
* be <b>null</b> if no priority was specified.
|
||||
*
|
||||
* @return the priority for newly created threads
|
||||
*/
|
||||
public final Integer getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code UncaughtExceptionHandler} for the threads created by
|
||||
* this factory. Result can be <b>null</b> if no handler was provided.
|
||||
*
|
||||
* @return the {@code UncaughtExceptionHandler}
|
||||
*/
|
||||
public final Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
|
||||
return uncaughtExceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of threads this factory has already created. This
|
||||
* class maintains an internal counter that is incremented each time the
|
||||
* {@link #newThread(Runnable)} method is invoked.
|
||||
*
|
||||
* @return the number of threads created by this factory
|
||||
*/
|
||||
public long getThreadCount() {
|
||||
return threadCounter.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new thread. This implementation delegates to the wrapped
|
||||
* factory for creating the thread. Then, on the newly created thread the
|
||||
* corresponding configuration options are set.
|
||||
*
|
||||
* @param runnable the {@code Runnable} to be executed by the new thread
|
||||
* @return the newly created thread
|
||||
*/
|
||||
@Override
|
||||
public Thread newThread(final Runnable runnable) {
|
||||
final Thread thread = getWrappedFactory().newThread(runnable);
|
||||
initializeThread(thread);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the specified thread. This method is called by
|
||||
* {@link #newThread(Runnable)} after a new thread has been obtained from
|
||||
* the wrapped thread factory. It initializes the thread according to the
|
||||
* options set for this factory.
|
||||
*
|
||||
* @param thread the thread to be initialized
|
||||
*/
|
||||
private void initializeThread(final Thread thread) {
|
||||
|
||||
if (getNamingPattern() != null) {
|
||||
final Long count = Long.valueOf(threadCounter.incrementAndGet());
|
||||
thread.setName(String.format(getNamingPattern(), count));
|
||||
}
|
||||
|
||||
if (getUncaughtExceptionHandler() != null) {
|
||||
thread.setUncaughtExceptionHandler(getUncaughtExceptionHandler());
|
||||
}
|
||||
|
||||
if (getPriority() != null) {
|
||||
thread.setPriority(getPriority().intValue());
|
||||
}
|
||||
|
||||
if (getDaemonFlag() != null) {
|
||||
thread.setDaemon(getDaemonFlag().booleanValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A <em>builder</em> class for creating instances of {@code
|
||||
* BasicThreadFactory}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using this builder class instances of {@code BasicThreadFactory} can be
|
||||
* created and initialized. The class provides methods that correspond to
|
||||
* the configuration options supported by {@code BasicThreadFactory}. Method
|
||||
* chaining is supported. Refer to the documentation of {@code
|
||||
* BasicThreadFactory} for a usage example.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public static class Builder
|
||||
implements org.apache.commons.lang3.builder.Builder<BasicThreadFactory> {
|
||||
|
||||
/** The wrapped factory. */
|
||||
private ThreadFactory wrappedFactory;
|
||||
|
||||
/** The uncaught exception handler. */
|
||||
private Thread.UncaughtExceptionHandler exceptionHandler;
|
||||
|
||||
/** The naming pattern. */
|
||||
private String namingPattern;
|
||||
|
||||
/** The priority. */
|
||||
private Integer priority;
|
||||
|
||||
/** The daemon flag. */
|
||||
private Boolean daemon;
|
||||
|
||||
/**
|
||||
* Sets the {@code ThreadFactory} to be wrapped by the new {@code
|
||||
* BasicThreadFactory}.
|
||||
*
|
||||
* @param factory the wrapped {@code ThreadFactory} (must not be
|
||||
* <b>null</b>)
|
||||
* @return a reference to this {@code Builder}
|
||||
* @throws NullPointerException if the passed in {@code ThreadFactory}
|
||||
* is <b>null</b>
|
||||
*/
|
||||
public Builder wrappedFactory(final ThreadFactory factory) {
|
||||
Validate.notNull(factory, "factory");
|
||||
|
||||
wrappedFactory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the naming pattern to be used by the new {@code
|
||||
* BasicThreadFactory}.
|
||||
*
|
||||
* @param pattern the naming pattern (must not be <b>null</b>)
|
||||
* @return a reference to this {@code Builder}
|
||||
* @throws NullPointerException if the naming pattern is <b>null</b>
|
||||
*/
|
||||
public Builder namingPattern(final String pattern) {
|
||||
Validate.notNull(pattern, "pattern");
|
||||
|
||||
namingPattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the daemon flag for the new {@code BasicThreadFactory}. If this
|
||||
* flag is set to <b>true</b> the new thread factory will create daemon
|
||||
* threads.
|
||||
*
|
||||
* @param daemon the value of the daemon flag
|
||||
* @return a reference to this {@code Builder}
|
||||
*/
|
||||
public Builder daemon(final boolean daemon) {
|
||||
this.daemon = Boolean.valueOf(daemon);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority for the threads created by the new {@code
|
||||
* BasicThreadFactory}.
|
||||
*
|
||||
* @param priority the priority
|
||||
* @return a reference to this {@code Builder}
|
||||
*/
|
||||
public Builder priority(final int priority) {
|
||||
this.priority = Integer.valueOf(priority);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the uncaught exception handler for the threads created by the
|
||||
* new {@code BasicThreadFactory}.
|
||||
*
|
||||
* @param handler the {@code UncaughtExceptionHandler} (must not be
|
||||
* <b>null</b>)
|
||||
* @return a reference to this {@code Builder}
|
||||
* @throws NullPointerException if the exception handler is <b>null</b>
|
||||
*/
|
||||
public Builder uncaughtExceptionHandler(
|
||||
final Thread.UncaughtExceptionHandler handler) {
|
||||
Validate.notNull(handler, "handler");
|
||||
|
||||
exceptionHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this builder. All configuration options are set to default
|
||||
* values. Note: If the {@link #build()} method was called, it is not
|
||||
* necessary to call {@code reset()} explicitly because this is done
|
||||
* automatically.
|
||||
*/
|
||||
public void reset() {
|
||||
wrappedFactory = null;
|
||||
exceptionHandler = null;
|
||||
namingPattern = null;
|
||||
priority = null;
|
||||
daemon = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code BasicThreadFactory} with all configuration
|
||||
* options that have been specified by calling methods on this builder.
|
||||
* After creating the factory {@link #reset()} is called.
|
||||
*
|
||||
* @return the new {@code BasicThreadFactory}
|
||||
*/
|
||||
@Override
|
||||
public BasicThreadFactory build() {
|
||||
final BasicThreadFactory factory = new BasicThreadFactory(this);
|
||||
reset();
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A specialized {@link BackgroundInitializer} implementation that wraps a
|
||||
* {@code Callable} object.
|
||||
* </p>
|
||||
* <p>
|
||||
* An instance of this class is initialized with a {@code Callable} object when
|
||||
* it is constructed. The implementation of the {@link #initialize()} method
|
||||
* defined in the super class delegates to this {@code Callable} so that the
|
||||
* {@code Callable} is executed in the background thread.
|
||||
* </p>
|
||||
* <p>
|
||||
* The {@code java.util.concurrent.Callable} interface is a standard mechanism
|
||||
* of the JDK to define tasks to be executed by another thread. The {@code
|
||||
* CallableBackgroundInitializer} class allows combining this standard interface
|
||||
* with the background initializer API.
|
||||
* </p>
|
||||
* <p>
|
||||
* Usage of this class is very similar to the default usage pattern of the
|
||||
* {@link BackgroundInitializer} class: Just create an instance and provide the
|
||||
* {@code Callable} object to be executed, then call the initializer's
|
||||
* {@link #start()} method. This causes the {@code Callable} to be executed in
|
||||
* another thread. When the results of the {@code Callable} are needed the
|
||||
* initializer's {@link #get()} method can be called (which may block until
|
||||
* background execution is complete). The following code fragment shows a
|
||||
* typical usage example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* // a Callable that performs a complex computation
|
||||
* Callable<Integer> computationCallable = new MyComputationCallable();
|
||||
* // setup the background initializer
|
||||
* CallableBackgroundInitializer<Integer> initializer =
|
||||
* new CallableBackgroundInitializer(computationCallable);
|
||||
* initializer.start();
|
||||
* // Now do some other things. Initialization runs in a parallel thread
|
||||
* ...
|
||||
* // Wait for the end of initialization and access the result
|
||||
* Integer result = initializer.get();
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public class CallableBackgroundInitializer<T> extends BackgroundInitializer<T> {
|
||||
/** The Callable to be executed. */
|
||||
private final Callable<T> callable;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CallableBackgroundInitializer} and sets
|
||||
* the {@code Callable} to be executed in a background thread.
|
||||
*
|
||||
* @param call the {@code Callable} (must not be <b>null</b>)
|
||||
* @throws IllegalArgumentException if the {@code Callable} is <b>null</b>
|
||||
*/
|
||||
public CallableBackgroundInitializer(final Callable<T> call) {
|
||||
checkCallable(call);
|
||||
callable = call;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CallableBackgroundInitializer} and
|
||||
* initializes it with the {@code Callable} to be executed in a background
|
||||
* thread and the {@code ExecutorService} for managing the background
|
||||
* execution.
|
||||
*
|
||||
* @param call the {@code Callable} (must not be <b>null</b>)
|
||||
* @param exec an external {@code ExecutorService} to be used for task
|
||||
* execution
|
||||
* @throws IllegalArgumentException if the {@code Callable} is <b>null</b>
|
||||
*/
|
||||
public CallableBackgroundInitializer(final Callable<T> call, final ExecutorService exec) {
|
||||
super(exec);
|
||||
checkCallable(call);
|
||||
callable = call;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs initialization in a background thread. This implementation
|
||||
* delegates to the {@code Callable} passed at construction time of this
|
||||
* object.
|
||||
*
|
||||
* @return the result of the initialization
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
@Override
|
||||
protected T initialize() throws Exception {
|
||||
return callable.call();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the passed in {@code Callable} and throws an exception if it is
|
||||
* undefined.
|
||||
*
|
||||
* @param callable the object to check
|
||||
* @throws IllegalArgumentException if the {@code Callable} is <b>null</b>
|
||||
*/
|
||||
private void checkCallable(final Callable<T> callable) {
|
||||
Validate.notNull(callable, "callable");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An interface describing a <a
|
||||
* href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> component.
|
||||
* </p>
|
||||
* <p>
|
||||
* A <em>circuit breaker</em> can be used to protect an application against unreliable
|
||||
* services or unexpected load. It typically monitors a specific resource. As long as this
|
||||
* resource works as expected, it stays in state <em>closed</em>, meaning that the
|
||||
* resource can be used. If problems are encountered when using the resource, the circuit
|
||||
* breaker can switch into state <em>open</em>; then access to this resource is
|
||||
* prohibited. Depending on a concrete implementation, it is possible that the circuit
|
||||
* breaker switches back to state <em>closed</em> when the resource becomes available
|
||||
* again.
|
||||
* </p>
|
||||
* <p>
|
||||
* This interface defines a generic protocol of a circuit breaker component. It should be
|
||||
* sufficiently generic to be applied to multiple different use cases.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the value monitored by this circuit breaker
|
||||
* @since 3.5
|
||||
*/
|
||||
public interface CircuitBreaker<T> {
|
||||
/**
|
||||
* Returns the current open state of this circuit breaker. A return value of
|
||||
* <strong>true</strong> means that the circuit breaker is currently open indicating a
|
||||
* problem in the monitored sub system.
|
||||
*
|
||||
* @return the current open state of this circuit breaker
|
||||
*/
|
||||
boolean isOpen();
|
||||
|
||||
/**
|
||||
* Returns the current closed state of this circuit breaker. A return value of
|
||||
* <strong>true</strong> means that the circuit breaker is currently closed. This
|
||||
* means that everything is okay with the monitored sub system.
|
||||
*
|
||||
* @return the current closed state of this circuit breaker
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Checks the state of this circuit breaker and changes it if necessary. The return
|
||||
* value indicates whether the circuit breaker is now in state {@code CLOSED}; a value
|
||||
* of <strong>true</strong> typically means that the current operation can continue.
|
||||
*
|
||||
* @return <strong>true</strong> if the circuit breaker is now closed;
|
||||
* <strong>false</strong> otherwise
|
||||
*/
|
||||
boolean checkState();
|
||||
|
||||
/**
|
||||
* Closes this circuit breaker. Its state is changed to closed. If this circuit
|
||||
* breaker is already closed, this method has no effect.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Opens this circuit breaker. Its state is changed to open. Depending on a concrete
|
||||
* implementation, it may close itself again if the monitored sub system becomes
|
||||
* available. If this circuit breaker is already open, this method has no effect.
|
||||
*/
|
||||
void open();
|
||||
|
||||
/**
|
||||
* Increments the monitored value and performs a check of the current state of this
|
||||
* circuit breaker. This method works like {@link #checkState()}, but the monitored
|
||||
* value is incremented before the state check is performed.
|
||||
*
|
||||
* @param increment value to increment in the monitored value of the circuit breaker
|
||||
* @return <strong>true</strong> if the circuit breaker is now closed;
|
||||
* <strong>false</strong> otherwise
|
||||
*/
|
||||
boolean incrementAndCheckState(T increment);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An exception class used for reporting runtime error conditions related to
|
||||
* circuit breakers.
|
||||
* </p>
|
||||
* @since 3.5
|
||||
*/
|
||||
public class CircuitBreakingException extends RuntimeException {
|
||||
/**
|
||||
* The serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1408176654686913340L;
|
||||
|
||||
/**
|
||||
* Creates a new, uninitialized instance of {@code CircuitBreakingException}.
|
||||
*/
|
||||
public CircuitBreakingException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CircuitBreakingException} and initializes it with the given message and cause.
|
||||
*
|
||||
* @param message the error message
|
||||
* @param cause the cause of this exception
|
||||
*/
|
||||
public CircuitBreakingException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CircuitBreakingException} and initializes it with the given message.
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
public CircuitBreakingException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CircuitBreakingException} and initializes it with the given cause.
|
||||
*
|
||||
* @param cause the cause of this exception
|
||||
*/
|
||||
public CircuitBreakingException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>Definition of an interface for a wrapper around a calculation that takes a single parameter and returns a result.</p>
|
||||
*
|
||||
* <p>This interface allows for wrapping a calculation into a class so that it maybe passed around an application.</p>
|
||||
*
|
||||
* @param <I> the type of the input to the calculation
|
||||
* @param <O> the type of the output of the calculation
|
||||
*
|
||||
* @since 3.6
|
||||
*/
|
||||
public interface Computable<I, O> {
|
||||
|
||||
/**
|
||||
* This method carries out the given operation with the provided argument.
|
||||
*
|
||||
* @param arg
|
||||
* the argument for the calculation
|
||||
* @return the result of the calculation
|
||||
* @throws InterruptedException
|
||||
* thrown if the calculation is interrupted
|
||||
*/
|
||||
O compute(I arg) throws InterruptedException;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An exception class used for reporting error conditions related to accessing
|
||||
* data of background tasks.
|
||||
* </p>
|
||||
* <p>
|
||||
* The purpose of this exception class is analogous to the default JDK exception
|
||||
* class {@link java.util.concurrent.ExecutionException}, i.e. it wraps an
|
||||
* exception that occurred during the execution of a task. However, in contrast
|
||||
* to {@code ExecutionException}, it wraps only checked exceptions. Runtime
|
||||
* exceptions are thrown directly.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConcurrentException extends Exception {
|
||||
/**
|
||||
* The serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 6622707671812226130L;
|
||||
|
||||
/**
|
||||
* Creates a new, uninitialized instance of {@code ConcurrentException}.
|
||||
*/
|
||||
protected ConcurrentException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ConcurrentException} and initializes it
|
||||
* with the given cause.
|
||||
*
|
||||
* @param cause the cause of this exception
|
||||
* @throws IllegalArgumentException if the cause is not a checked exception
|
||||
*/
|
||||
public ConcurrentException(final Throwable cause) {
|
||||
super(ConcurrentUtils.checkedException(cause));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ConcurrentException} and initializes it
|
||||
* with the given message and cause.
|
||||
*
|
||||
* @param msg the error message
|
||||
* @param cause the cause of this exception
|
||||
* @throws IllegalArgumentException if the cause is not a checked exception
|
||||
*/
|
||||
public ConcurrentException(final String msg, final Throwable cause) {
|
||||
super(msg, ConcurrentUtils.checkedException(cause));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Definition of an interface for the thread-safe initialization of objects.
|
||||
* </p>
|
||||
* <p>
|
||||
* The idea behind this interface is to provide access to an object in a
|
||||
* thread-safe manner. A {@code ConcurrentInitializer} can be passed to multiple
|
||||
* threads which can all access the object produced by the initializer. Through
|
||||
* the {@link #get()} method the object can be queried.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete implementations of this interface will use different strategies for
|
||||
* the creation of the managed object, e.g. lazy initialization or
|
||||
* initialization in a background thread. This is completely transparent to
|
||||
* client code, so it is possible to change the initialization strategy without
|
||||
* affecting clients.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public interface ConcurrentInitializer<T> {
|
||||
/**
|
||||
* Returns the fully initialized object produced by this {@code
|
||||
* ConcurrentInitializer}. A concrete implementation here returns the
|
||||
* results of the initialization process. This method may block until
|
||||
* results are available. Typically, once created the result object is
|
||||
* always the same.
|
||||
*
|
||||
* @return the object created by this {@code ConcurrentException}
|
||||
* @throws ConcurrentException if an error occurred during initialization of
|
||||
* the object
|
||||
*/
|
||||
T get() throws ConcurrentException;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An exception class used for reporting runtime error conditions related to
|
||||
* accessing data of background tasks.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class is an analogue of the {@link ConcurrentException} exception class.
|
||||
* However, it is a runtime exception and thus does not need explicit catch
|
||||
* clauses. Some methods of {@link ConcurrentUtils} throw {@code
|
||||
* ConcurrentRuntimeException} exceptions rather than
|
||||
* {@link ConcurrentException} exceptions. They can be used by client code that
|
||||
* does not want to be bothered with checked exceptions.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConcurrentRuntimeException extends RuntimeException {
|
||||
/**
|
||||
* The serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = -6582182735562919670L;
|
||||
|
||||
/**
|
||||
* Creates a new, uninitialized instance of {@code
|
||||
* ConcurrentRuntimeException}.
|
||||
*/
|
||||
protected ConcurrentRuntimeException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ConcurrentRuntimeException} and
|
||||
* initializes it with the given cause.
|
||||
*
|
||||
* @param cause the cause of this exception
|
||||
* @throws IllegalArgumentException if the cause is not a checked exception
|
||||
*/
|
||||
public ConcurrentRuntimeException(final Throwable cause) {
|
||||
super(ConcurrentUtils.checkedException(cause));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ConcurrentRuntimeException} and
|
||||
* initializes it with the given message and cause.
|
||||
*
|
||||
* @param msg the error message
|
||||
* @param cause the cause of this exception
|
||||
* @throws IllegalArgumentException if the cause is not a checked exception
|
||||
*/
|
||||
public ConcurrentRuntimeException(final String msg, final Throwable cause) {
|
||||
super(msg, ConcurrentUtils.checkedException(cause));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An utility class providing functionality related to the {@code
|
||||
* java.util.concurrent} package.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConcurrentUtils {
|
||||
|
||||
/**
|
||||
* Private constructor so that no instances can be created. This class
|
||||
* contains only static utility methods.
|
||||
*/
|
||||
private ConcurrentUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the cause of the specified {@code ExecutionException} and
|
||||
* creates a {@code ConcurrentException} with the checked cause if
|
||||
* necessary. This method performs the following checks on the cause of the
|
||||
* passed in exception:
|
||||
* <ul>
|
||||
* <li>If the passed in exception is <b>null</b> or the cause is
|
||||
* <b>null</b>, this method returns <b>null</b>.</li>
|
||||
* <li>If the cause is a runtime exception, it is directly thrown.</li>
|
||||
* <li>If the cause is an error, it is directly thrown, too.</li>
|
||||
* <li>In any other case the cause is a checked exception. The method then
|
||||
* creates a {@link ConcurrentException}, initializes it with the cause, and
|
||||
* returns it.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ex the exception to be processed
|
||||
* @return a {@code ConcurrentException} with the checked cause
|
||||
*/
|
||||
public static ConcurrentException extractCause(final ExecutionException ex) {
|
||||
if (ex == null || ex.getCause() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throwCause(ex);
|
||||
return new ConcurrentException(ex.getMessage(), ex.getCause());
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the cause of the specified {@code ExecutionException} and
|
||||
* creates a {@code ConcurrentRuntimeException} with the checked cause if
|
||||
* necessary. This method works exactly like
|
||||
* {@link #extractCause(ExecutionException)}. The only difference is that
|
||||
* the cause of the specified {@code ExecutionException} is extracted as a
|
||||
* runtime exception. This is an alternative for client code that does not
|
||||
* want to deal with checked exceptions.
|
||||
*
|
||||
* @param ex the exception to be processed
|
||||
* @return a {@code ConcurrentRuntimeException} with the checked cause
|
||||
*/
|
||||
public static ConcurrentRuntimeException extractCauseUnchecked(
|
||||
final ExecutionException ex) {
|
||||
if (ex == null || ex.getCause() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throwCause(ex);
|
||||
return new ConcurrentRuntimeException(ex.getMessage(), ex.getCause());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the specified {@code ExecutionException}. This method calls
|
||||
* {@link #extractCause(ExecutionException)} for obtaining the cause of the
|
||||
* exception - which might already cause an unchecked exception or an error
|
||||
* being thrown. If the cause is a checked exception however, it is wrapped
|
||||
* in a {@code ConcurrentException}, which is thrown. If the passed in
|
||||
* exception is <b>null</b> or has no cause, the method simply returns
|
||||
* without throwing an exception.
|
||||
*
|
||||
* @param ex the exception to be handled
|
||||
* @throws ConcurrentException if the cause of the {@code
|
||||
* ExecutionException} is a checked exception
|
||||
*/
|
||||
public static void handleCause(final ExecutionException ex)
|
||||
throws ConcurrentException {
|
||||
final ConcurrentException cex = extractCause(ex);
|
||||
|
||||
if (cex != null) {
|
||||
throw cex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the specified {@code ExecutionException} and transforms it into a
|
||||
* runtime exception. This method works exactly like
|
||||
* {@link #handleCause(ExecutionException)}, but instead of a
|
||||
* {@link ConcurrentException} it throws a
|
||||
* {@link ConcurrentRuntimeException}. This is an alternative for client
|
||||
* code that does not want to deal with checked exceptions.
|
||||
*
|
||||
* @param ex the exception to be handled
|
||||
* @throws ConcurrentRuntimeException if the cause of the {@code
|
||||
* ExecutionException} is a checked exception; this exception is then
|
||||
* wrapped in the thrown runtime exception
|
||||
*/
|
||||
public static void handleCauseUnchecked(final ExecutionException ex) {
|
||||
final ConcurrentRuntimeException crex = extractCauseUnchecked(ex);
|
||||
|
||||
if (crex != null) {
|
||||
throw crex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the specified {@code Throwable} is a checked exception. If
|
||||
* not, an exception is thrown.
|
||||
*
|
||||
* @param ex the {@code Throwable} to check
|
||||
* @return a flag whether the passed in exception is a checked exception
|
||||
* @throws IllegalArgumentException if the {@code Throwable} is not a
|
||||
* checked exception
|
||||
*/
|
||||
static Throwable checkedException(final Throwable ex) {
|
||||
Validate.isTrue(ex != null && !(ex instanceof RuntimeException)
|
||||
&& !(ex instanceof Error), "Not a checked exception: " + ex);
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the cause of the specified {@code ExecutionException}
|
||||
* should be thrown and does it if necessary.
|
||||
*
|
||||
* @param ex the exception in question
|
||||
*/
|
||||
private static void throwCause(final ExecutionException ex) {
|
||||
if (ex.getCause() instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex.getCause();
|
||||
}
|
||||
|
||||
if (ex.getCause() instanceof Error) {
|
||||
throw (Error) ex.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Invokes the specified {@code ConcurrentInitializer} and returns the
|
||||
* object produced by the initializer. This method just invokes the {@code
|
||||
* get()} method of the given {@code ConcurrentInitializer}. It is
|
||||
* <b>null</b>-safe: if the argument is <b>null</b>, result is also
|
||||
* <b>null</b>.
|
||||
*
|
||||
* @param <T> the type of the object produced by the initializer
|
||||
* @param initializer the {@code ConcurrentInitializer} to be invoked
|
||||
* @return the object managed by the {@code ConcurrentInitializer}
|
||||
* @throws ConcurrentException if the {@code ConcurrentInitializer} throws
|
||||
* an exception
|
||||
*/
|
||||
public static <T> T initialize(final ConcurrentInitializer<T> initializer)
|
||||
throws ConcurrentException {
|
||||
return initializer != null ? initializer.get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the specified {@code ConcurrentInitializer} and transforms
|
||||
* occurring exceptions to runtime exceptions. This method works like
|
||||
* {@link #initialize(ConcurrentInitializer)}, but if the {@code
|
||||
* ConcurrentInitializer} throws a {@link ConcurrentException}, it is
|
||||
* caught, and the cause is wrapped in a {@link ConcurrentRuntimeException}.
|
||||
* So client code does not have to deal with checked exceptions.
|
||||
*
|
||||
* @param <T> the type of the object produced by the initializer
|
||||
* @param initializer the {@code ConcurrentInitializer} to be invoked
|
||||
* @return the object managed by the {@code ConcurrentInitializer}
|
||||
* @throws ConcurrentRuntimeException if the initializer throws an exception
|
||||
*/
|
||||
public static <T> T initializeUnchecked(final ConcurrentInitializer<T> initializer) {
|
||||
try {
|
||||
return initialize(initializer);
|
||||
} catch (final ConcurrentException cex) {
|
||||
throw new ConcurrentRuntimeException(cex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>
|
||||
* Puts a value in the specified {@code ConcurrentMap} if the key is not yet
|
||||
* present. This method works similar to the {@code putIfAbsent()} method of
|
||||
* the {@code ConcurrentMap} interface, but the value returned is different.
|
||||
* Basically, this method is equivalent to the following code fragment:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* if (!map.containsKey(key)) {
|
||||
* map.put(key, value);
|
||||
* return value;
|
||||
* } else {
|
||||
* return map.get(key);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* except that the action is performed atomically. So this method always
|
||||
* returns the value which is stored in the map.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method is <b>null</b>-safe: It accepts a <b>null</b> map as input
|
||||
* without throwing an exception. In this case the return value is
|
||||
* <b>null</b>, too.
|
||||
* </p>
|
||||
*
|
||||
* @param <K> the type of the keys of the map
|
||||
* @param <V> the type of the values of the map
|
||||
* @param map the map to be modified
|
||||
* @param key the key of the value to be added
|
||||
* @param value the value to be added
|
||||
* @return the value stored in the map after this operation
|
||||
*/
|
||||
public static <K, V> V putIfAbsent(final ConcurrentMap<K, V> map, final K key, final V value) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final V result = map.putIfAbsent(key, value);
|
||||
return result != null ? result : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a concurrent map contains a key and creates a corresponding
|
||||
* value if not. This method first checks the presence of the key in the
|
||||
* given map. If it is already contained, its value is returned. Otherwise
|
||||
* the {@code get()} method of the passed in {@link ConcurrentInitializer}
|
||||
* is called. With the resulting object
|
||||
* {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This
|
||||
* handles the case that in the meantime another thread has added the key to
|
||||
* the map. Both the map and the initializer can be <b>null</b>; in this
|
||||
* case this method simply returns <b>null</b>.
|
||||
*
|
||||
* @param <K> the type of the keys of the map
|
||||
* @param <V> the type of the values of the map
|
||||
* @param map the map to be modified
|
||||
* @param key the key of the value to be added
|
||||
* @param init the {@link ConcurrentInitializer} for creating the value
|
||||
* @return the value stored in the map after this operation; this may or may
|
||||
* not be the object created by the {@link ConcurrentInitializer}
|
||||
* @throws ConcurrentException if the initializer throws an exception
|
||||
*/
|
||||
public static <K, V> V createIfAbsent(final ConcurrentMap<K, V> map, final K key,
|
||||
final ConcurrentInitializer<V> init) throws ConcurrentException {
|
||||
if (map == null || init == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final V value = map.get(key);
|
||||
if (value == null) {
|
||||
return putIfAbsent(map, key, init.get());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a concurrent map contains a key and creates a corresponding
|
||||
* value if not, suppressing checked exceptions. This method calls
|
||||
* {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it
|
||||
* is caught and re-thrown as a {@link ConcurrentRuntimeException}.
|
||||
*
|
||||
* @param <K> the type of the keys of the map
|
||||
* @param <V> the type of the values of the map
|
||||
* @param map the map to be modified
|
||||
* @param key the key of the value to be added
|
||||
* @param init the {@link ConcurrentInitializer} for creating the value
|
||||
* @return the value stored in the map after this operation; this may or may
|
||||
* not be the object created by the {@link ConcurrentInitializer}
|
||||
* @throws ConcurrentRuntimeException if the initializer throws an exception
|
||||
*/
|
||||
public static <K, V> V createIfAbsentUnchecked(final ConcurrentMap<K, V> map,
|
||||
final K key, final ConcurrentInitializer<V> init) {
|
||||
try {
|
||||
return createIfAbsent(map, key, init);
|
||||
} catch (final ConcurrentException cex) {
|
||||
throw new ConcurrentRuntimeException(cex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>
|
||||
* Gets an implementation of {@code Future} that is immediately done
|
||||
* and returns the specified constant value.
|
||||
* </p>
|
||||
* <p>
|
||||
* This can be useful to return a simple constant immediately from the
|
||||
* concurrent processing, perhaps as part of avoiding nulls.
|
||||
* A constant future can also be useful in testing.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the value used by this {@code Future} object
|
||||
* @param value the constant value to return, may be null
|
||||
* @return an instance of Future that will return the value, never null
|
||||
*/
|
||||
public static <T> Future<T> constantFuture(final T value) {
|
||||
return new ConstantFuture<>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* A specialized {@code Future} implementation which wraps a constant value.
|
||||
* @param <T> the type of the value wrapped by this class
|
||||
*/
|
||||
static final class ConstantFuture<T> implements Future<T> {
|
||||
/** The constant value. */
|
||||
private final T value;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ConstantFuture} and initializes it
|
||||
* with the constant value.
|
||||
*
|
||||
* @param value the value (may be <b>null</b>)
|
||||
*/
|
||||
ConstantFuture(final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} This implementation always returns <b>true</b> because
|
||||
* the constant object managed by this {@code Future} implementation is
|
||||
* always available.
|
||||
*/
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} This implementation just returns the constant value.
|
||||
*/
|
||||
@Override
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} This implementation just returns the constant value; it
|
||||
* does not block, therefore the timeout has no meaning.
|
||||
*/
|
||||
@Override
|
||||
public T get(final long timeout, final TimeUnit unit) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} This implementation always returns <b>false</b>; there
|
||||
* is no background process which could be cancelled.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} The cancel operation is not supported. This
|
||||
* implementation always returns <b>false</b>.
|
||||
*/
|
||||
@Override
|
||||
public boolean cancel(final boolean mayInterruptIfRunning) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A very simple implementation of the {@link ConcurrentInitializer} interface
|
||||
* which always returns the same object.
|
||||
* </p>
|
||||
* <p>
|
||||
* An instance of this class is passed a reference to an object when it is
|
||||
* constructed. The {@link #get()} method just returns this object. No
|
||||
* synchronization is required.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class is useful for instance for unit testing or in cases where a
|
||||
* specific object has to be passed to an object which expects a
|
||||
* {@link ConcurrentInitializer}.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer
|
||||
*/
|
||||
public class ConstantInitializer<T> implements ConcurrentInitializer<T> {
|
||||
/** Constant for the format of the string representation. */
|
||||
private static final String FMT_TO_STRING = "ConstantInitializer@%d [ object = %s ]";
|
||||
|
||||
/** Stores the managed object. */
|
||||
private final T object;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code ConstantInitializer} and initializes it
|
||||
* with the object to be managed. The {@code get()} method will always
|
||||
* return the object passed here. This class does not place any restrictions
|
||||
* on the object. It may be <b>null</b>, then {@code get()} will return
|
||||
* <b>null</b>, too.
|
||||
*
|
||||
* @param obj the object to be managed by this initializer
|
||||
*/
|
||||
public ConstantInitializer(final T obj) {
|
||||
object = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly returns the object that was passed to the constructor. This is
|
||||
* the same object as returned by {@code get()}. However, this method does
|
||||
* not declare that it throws an exception.
|
||||
*
|
||||
* @return the object managed by this initializer
|
||||
*/
|
||||
public final T getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object managed by this initializer. This implementation just
|
||||
* returns the object passed to the constructor.
|
||||
*
|
||||
* @return the object managed by this initializer
|
||||
* @throws ConcurrentException if an error occurs
|
||||
*/
|
||||
@Override
|
||||
public T get() throws ConcurrentException {
|
||||
return getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code for this object. This implementation returns the hash
|
||||
* code of the managed object.
|
||||
*
|
||||
* @return a hash code for this object
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getObject() != null ? getObject().hashCode() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this object with another one. This implementation returns
|
||||
* <b>true</b> if and only if the passed in object is an instance of
|
||||
* {@code ConstantInitializer} which refers to an object equals to the
|
||||
* object managed by this instance.
|
||||
*
|
||||
* @param obj the object to compare to
|
||||
* @return a flag whether the objects are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ConstantInitializer<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ConstantInitializer<?> c = (ConstantInitializer<?>) obj;
|
||||
return Objects.equals(getObject(), c.getObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation for this object. This string also
|
||||
* contains a string representation of the object managed by this
|
||||
* initializer.
|
||||
*
|
||||
* @return a string for this object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(FMT_TO_STRING, Integer.valueOf(System.identityHashCode(this)),
|
||||
String.valueOf(getObject()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,565 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A simple implementation of the <a
|
||||
* href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern
|
||||
* that counts specific events.
|
||||
* </p>
|
||||
* <p>
|
||||
* A <em>circuit breaker</em> can be used to protect an application against unreliable
|
||||
* services or unexpected load. A newly created {@code EventCountCircuitBreaker} object is
|
||||
* initially in state <em>closed</em> meaning that no problem has been detected. When the
|
||||
* application encounters specific events (like errors or service timeouts), it tells the
|
||||
* circuit breaker to increment an internal counter. If the number of events reported in a
|
||||
* specific time interval exceeds a configurable threshold, the circuit breaker changes
|
||||
* into state <em>open</em>. This means that there is a problem with the associated sub
|
||||
* system; the application should no longer call it, but give it some time to settle down.
|
||||
* The circuit breaker can be configured to switch back to <em>closed</em> state after a
|
||||
* certain time frame if the number of events received goes below a threshold.
|
||||
* </p>
|
||||
* <p>
|
||||
* When a {@code EventCountCircuitBreaker} object is constructed the following parameters
|
||||
* can be provided:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>A threshold for the number of events that causes a state transition to
|
||||
* <em>open</em> state. If more events are received in the configured check interval, the
|
||||
* circuit breaker switches to <em>open</em> state.</li>
|
||||
* <li>The interval for checks whether the circuit breaker should open. So it is possible
|
||||
* to specify something like "The circuit breaker should open if more than 10 errors are
|
||||
* encountered in a minute."</li>
|
||||
* <li>The same parameters can be specified for automatically closing the circuit breaker
|
||||
* again, as in "If the number of requests goes down to 100 per minute, the circuit
|
||||
* breaker should close itself again". Depending on the use case, it may make sense to use
|
||||
* a slightly lower threshold for closing the circuit breaker than for opening it to avoid
|
||||
* continuously flipping when the number of events received is close to the threshold.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class supports the following typical use cases:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>Protecting against load peaks</strong>
|
||||
* </p>
|
||||
* <p>
|
||||
* Imagine you have a server which can handle a certain number of requests per minute.
|
||||
* Suddenly, the number of requests increases significantly - maybe because a connected
|
||||
* partner system is going mad or due to a denial of service attack. A
|
||||
* {@code EventCountCircuitBreaker} can be configured to stop the application from
|
||||
* processing requests when a sudden peak load is detected and to start request processing
|
||||
* again when things calm down. The following code fragment shows a typical example of
|
||||
* such a scenario. Here the {@code EventCountCircuitBreaker} allows up to 1000 requests
|
||||
* per minute before it interferes. When the load goes down again to 800 requests per
|
||||
* second it switches back to state <em>closed</em>:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(1000, 1, TimeUnit.MINUTE, 800);
|
||||
* ...
|
||||
* public void handleRequest(Request request) {
|
||||
* if (breaker.incrementAndCheckState()) {
|
||||
* // actually handle this request
|
||||
* } else {
|
||||
* // do something else, e.g. send an error code
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* <strong>Deal with an unreliable service</strong>
|
||||
* </p>
|
||||
* <p>
|
||||
* In this scenario, an application uses an external service which may fail from time to
|
||||
* time. If there are too many errors, the service is considered down and should not be
|
||||
* called for a while. This can be achieved using the following pattern - in this concrete
|
||||
* example we accept up to 5 errors in 2 minutes; if this limit is reached, the service is
|
||||
* given a rest time of 10 minutes:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(5, 2, TimeUnit.MINUTE, 5, 10, TimeUnit.MINUTE);
|
||||
* ...
|
||||
* public void handleRequest(Request request) {
|
||||
* if (breaker.checkState()) {
|
||||
* try {
|
||||
* service.doSomething();
|
||||
* } catch (ServiceException ex) {
|
||||
* breaker.incrementAndCheckState();
|
||||
* }
|
||||
* } else {
|
||||
* // return an error code, use an alternative service, etc.
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* In addition to automatic state transitions, the state of a circuit breaker can be
|
||||
* changed manually using the methods {@link #open()} and {@link #close()}. It is also
|
||||
* possible to register {@code PropertyChangeListener} objects that get notified whenever
|
||||
* a state transition occurs. This is useful, for instance to directly react on a freshly
|
||||
* detected error condition.
|
||||
* </p>
|
||||
* <p>
|
||||
* <em>Implementation notes:</em>
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>This implementation uses non-blocking algorithms to update the internal counter and
|
||||
* state. This should be pretty efficient if there is not too much contention.</li>
|
||||
* <li>This implementation is not intended to operate as a high-precision timer in very
|
||||
* short check intervals. It is deliberately kept simple to avoid complex and
|
||||
* time-consuming state checks. It should work well in time intervals from a few seconds
|
||||
* up to minutes and longer. If the intervals become too short, there might be race
|
||||
* conditions causing spurious state transitions.</li>
|
||||
* <li>The handling of check intervals is a bit simplistic. Therefore, there is no
|
||||
* guarantee that the circuit breaker is triggered at a specific point in time; there may
|
||||
* be some delay (less than a check interval).</li>
|
||||
* </ul>
|
||||
* @since 3.5
|
||||
*/
|
||||
public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
|
||||
|
||||
/** A map for accessing the strategy objects for the different states. */
|
||||
private static final Map<State, StateStrategy> STRATEGY_MAP = createStrategyMap();
|
||||
|
||||
/** Stores information about the current check interval. */
|
||||
private final AtomicReference<CheckIntervalData> checkIntervalData;
|
||||
|
||||
/** The threshold for opening the circuit breaker. */
|
||||
private final int openingThreshold;
|
||||
|
||||
/** The time interval for opening the circuit breaker. */
|
||||
private final long openingInterval;
|
||||
|
||||
/** The threshold for closing the circuit breaker. */
|
||||
private final int closingThreshold;
|
||||
|
||||
/** The time interval for closing the circuit breaker. */
|
||||
private final long closingInterval;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code EventCountCircuitBreaker} and initializes all properties for
|
||||
* opening and closing it based on threshold values for events occurring in specific
|
||||
* intervals.
|
||||
*
|
||||
* @param openingThreshold the threshold for opening the circuit breaker; if this
|
||||
* number of events is received in the time span determined by the opening interval,
|
||||
* the circuit breaker is opened
|
||||
* @param openingInterval the interval for opening the circuit breaker
|
||||
* @param openingUnit the {@code TimeUnit} defining the opening interval
|
||||
* @param closingThreshold the threshold for closing the circuit breaker; if the
|
||||
* number of events received in the time span determined by the closing interval goes
|
||||
* below this threshold, the circuit breaker is closed again
|
||||
* @param closingInterval the interval for closing the circuit breaker
|
||||
* @param closingUnit the {@code TimeUnit} defining the closing interval
|
||||
*/
|
||||
public EventCountCircuitBreaker(final int openingThreshold, final long openingInterval,
|
||||
final TimeUnit openingUnit, final int closingThreshold, final long closingInterval,
|
||||
final TimeUnit closingUnit) {
|
||||
checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0));
|
||||
this.openingThreshold = openingThreshold;
|
||||
this.openingInterval = openingUnit.toNanos(openingInterval);
|
||||
this.closingThreshold = closingThreshold;
|
||||
this.closingInterval = closingUnit.toNanos(closingInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code EventCountCircuitBreaker} with the same interval for opening
|
||||
* and closing checks.
|
||||
*
|
||||
* @param openingThreshold the threshold for opening the circuit breaker; if this
|
||||
* number of events is received in the time span determined by the check interval, the
|
||||
* circuit breaker is opened
|
||||
* @param checkInterval the check interval for opening or closing the circuit breaker
|
||||
* @param checkUnit the {@code TimeUnit} defining the check interval
|
||||
* @param closingThreshold the threshold for closing the circuit breaker; if the
|
||||
* number of events received in the time span determined by the check interval goes
|
||||
* below this threshold, the circuit breaker is closed again
|
||||
*/
|
||||
public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit,
|
||||
final int closingThreshold) {
|
||||
this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval,
|
||||
checkUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code EventCountCircuitBreaker} which uses the same parameters for
|
||||
* opening and closing checks.
|
||||
*
|
||||
* @param threshold the threshold for changing the status of the circuit breaker; if
|
||||
* the number of events received in a check interval is greater than this value, the
|
||||
* circuit breaker is opened; if it is lower than this value, it is closed again
|
||||
* @param checkInterval the check interval for opening or closing the circuit breaker
|
||||
* @param checkUnit the {@code TimeUnit} defining the check interval
|
||||
*/
|
||||
public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) {
|
||||
this(threshold, checkInterval, checkUnit, threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the threshold value for opening the circuit breaker. If this number of
|
||||
* events is received in the time span determined by the opening interval, the circuit
|
||||
* breaker is opened.
|
||||
*
|
||||
* @return the opening threshold
|
||||
*/
|
||||
public int getOpeningThreshold() {
|
||||
return openingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interval (in nanoseconds) for checking for the opening threshold.
|
||||
*
|
||||
* @return the opening check interval
|
||||
*/
|
||||
public long getOpeningInterval() {
|
||||
return openingInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the threshold value for closing the circuit breaker. If the number of
|
||||
* events received in the time span determined by the closing interval goes below this
|
||||
* threshold, the circuit breaker is closed again.
|
||||
*
|
||||
* @return the closing threshold
|
||||
*/
|
||||
public int getClosingThreshold() {
|
||||
return closingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interval (in nanoseconds) for checking for the closing threshold.
|
||||
*
|
||||
* @return the opening check interval
|
||||
*/
|
||||
public long getClosingInterval() {
|
||||
return closingInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} This implementation checks the internal event counter against the
|
||||
* threshold values and the check intervals. This may cause a state change of this
|
||||
* circuit breaker.
|
||||
*/
|
||||
@Override
|
||||
public boolean checkState() {
|
||||
return performStateCheck(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean incrementAndCheckState(final Integer increment) {
|
||||
return performStateCheck(increment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the monitored value by <strong>1</strong> and performs a check of the current state of this
|
||||
* circuit breaker. This method works like {@link #checkState()}, but the monitored
|
||||
* value is incremented before the state check is performed.
|
||||
*
|
||||
* @return <strong>true</strong> if the circuit breaker is now closed;
|
||||
* <strong>false</strong> otherwise
|
||||
*/
|
||||
public boolean incrementAndCheckState() {
|
||||
return incrementAndCheckState(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} This circuit breaker may close itself again if the number of events
|
||||
* received during a check interval goes below the closing threshold. If this circuit
|
||||
* breaker is already open, this method has no effect, except that a new check
|
||||
* interval is started.
|
||||
*/
|
||||
@Override
|
||||
public void open() {
|
||||
super.open();
|
||||
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} A new check interval is started. If too many events are received in
|
||||
* this interval, the circuit breaker changes again to state open. If this circuit
|
||||
* breaker is already closed, this method has no effect, except that a new check
|
||||
* interval is started.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually checks the state of this circuit breaker and executes a state transition
|
||||
* if necessary.
|
||||
*
|
||||
* @param increment the increment for the internal counter
|
||||
* @return a flag whether the circuit breaker is now closed
|
||||
*/
|
||||
private boolean performStateCheck(final int increment) {
|
||||
CheckIntervalData currentData;
|
||||
CheckIntervalData nextData;
|
||||
State currentState;
|
||||
|
||||
do {
|
||||
final long time = nanoTime();
|
||||
currentState = state.get();
|
||||
currentData = checkIntervalData.get();
|
||||
nextData = nextCheckIntervalData(increment, currentData, currentState, time);
|
||||
} while (!updateCheckIntervalData(currentData, nextData));
|
||||
|
||||
// This might cause a race condition if other changes happen in between!
|
||||
// Refer to the header comment!
|
||||
if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) {
|
||||
currentState = currentState.oppositeState();
|
||||
changeStateAndStartNewCheckInterval(currentState);
|
||||
}
|
||||
return !isOpen(currentState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@code CheckIntervalData} object. The current data object is replaced
|
||||
* by the one modified by the last check. The return value indicates whether this was
|
||||
* successful. If it is <strong>false</strong>, another thread interfered, and the
|
||||
* whole operation has to be redone.
|
||||
*
|
||||
* @param currentData the current check data object
|
||||
* @param nextData the replacing check data object
|
||||
* @return a flag whether the update was successful
|
||||
*/
|
||||
private boolean updateCheckIntervalData(final CheckIntervalData currentData,
|
||||
final CheckIntervalData nextData) {
|
||||
return currentData == nextData
|
||||
|| checkIntervalData.compareAndSet(currentData, nextData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the state of this circuit breaker and also initializes a new
|
||||
* {@code CheckIntervalData} object.
|
||||
*
|
||||
* @param newState the new state to be set
|
||||
*/
|
||||
private void changeStateAndStartNewCheckInterval(final State newState) {
|
||||
changeState(newState);
|
||||
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next {@code CheckIntervalData} object based on the current data and
|
||||
* the current state. The next data object takes the counter increment and the current
|
||||
* time into account.
|
||||
*
|
||||
* @param increment the increment for the internal counter
|
||||
* @param currentData the current check data object
|
||||
* @param currentState the current state of the circuit breaker
|
||||
* @param time the current time
|
||||
* @return the updated {@code CheckIntervalData} object
|
||||
*/
|
||||
private CheckIntervalData nextCheckIntervalData(final int increment,
|
||||
final CheckIntervalData currentData, final State currentState, final long time) {
|
||||
final CheckIntervalData nextData;
|
||||
if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
|
||||
nextData = new CheckIntervalData(increment, time);
|
||||
} else {
|
||||
nextData = currentData.increment(increment);
|
||||
}
|
||||
return nextData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time in nanoseconds. This method is used to obtain the current
|
||||
* time. This is needed to calculate the check intervals correctly.
|
||||
*
|
||||
* @return the current time in nanoseconds
|
||||
*/
|
||||
long nanoTime() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code StateStrategy} object responsible for the given state.
|
||||
*
|
||||
* @param state the state
|
||||
* @return the corresponding {@code StateStrategy}
|
||||
* @throws CircuitBreakingException if the strategy cannot be resolved
|
||||
*/
|
||||
private static StateStrategy stateStrategy(final State state) {
|
||||
return STRATEGY_MAP.get(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the map with strategy objects. It allows access for a strategy for a given
|
||||
* state.
|
||||
*
|
||||
* @return the strategy map
|
||||
*/
|
||||
private static Map<State, StateStrategy> createStrategyMap() {
|
||||
final Map<State, StateStrategy> map = new EnumMap<>(State.class);
|
||||
map.put(State.CLOSED, new StateStrategyClosed());
|
||||
map.put(State.OPEN, new StateStrategyOpen());
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* An internally used data class holding information about the checks performed by
|
||||
* this class. Basically, the number of received events and the start time of the
|
||||
* current check interval are stored.
|
||||
*/
|
||||
private static class CheckIntervalData {
|
||||
/** The counter for events. */
|
||||
private final int eventCount;
|
||||
|
||||
/** The start time of the current check interval. */
|
||||
private final long checkIntervalStart;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CheckIntervalData}.
|
||||
*
|
||||
* @param count the current count value
|
||||
* @param intervalStart the start time of the check interval
|
||||
*/
|
||||
CheckIntervalData(final int count, final long intervalStart) {
|
||||
eventCount = count;
|
||||
checkIntervalStart = intervalStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event counter.
|
||||
*
|
||||
* @return the number of received events
|
||||
*/
|
||||
public int getEventCount() {
|
||||
return eventCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start time of the current check interval.
|
||||
*
|
||||
* @return the check interval start time
|
||||
*/
|
||||
public long getCheckIntervalStart() {
|
||||
return checkIntervalStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance of {@code CheckIntervalData} with the event counter
|
||||
* incremented by the given delta. If the delta is 0, this object is returned.
|
||||
*
|
||||
* @param delta the delta
|
||||
* @return the updated instance
|
||||
*/
|
||||
public CheckIntervalData increment(final int delta) {
|
||||
return (delta == 0) ? this : new CheckIntervalData(getEventCount() + delta,
|
||||
getCheckIntervalStart());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally used class for executing check logic based on the current state of the
|
||||
* circuit breaker. Having this logic extracted into special classes avoids complex
|
||||
* if-then-else cascades.
|
||||
*/
|
||||
private abstract static class StateStrategy {
|
||||
/**
|
||||
* Returns a flag whether the end of the current check interval is reached.
|
||||
*
|
||||
* @param breaker the {@code CircuitBreaker}
|
||||
* @param currentData the current state object
|
||||
* @param now the current time
|
||||
* @return a flag whether the end of the current check interval is reached
|
||||
*/
|
||||
public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker,
|
||||
final CheckIntervalData currentData, final long now) {
|
||||
return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified {@code CheckIntervalData} objects indicate that a
|
||||
* state transition should occur. Here the logic which checks for thresholds
|
||||
* depending on the current state is implemented.
|
||||
*
|
||||
* @param breaker the {@code CircuitBreaker}
|
||||
* @param currentData the current {@code CheckIntervalData} object
|
||||
* @param nextData the updated {@code CheckIntervalData} object
|
||||
* @return a flag whether a state transition should be performed
|
||||
*/
|
||||
public abstract boolean isStateTransition(EventCountCircuitBreaker breaker,
|
||||
CheckIntervalData currentData, CheckIntervalData nextData);
|
||||
|
||||
/**
|
||||
* Obtains the check interval to applied for the represented state from the given
|
||||
* {@code CircuitBreaker}.
|
||||
*
|
||||
* @param breaker the {@code CircuitBreaker}
|
||||
* @return the check interval to be applied
|
||||
*/
|
||||
protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker);
|
||||
}
|
||||
|
||||
/**
|
||||
* A specialized {@code StateStrategy} implementation for the state closed.
|
||||
*/
|
||||
private static class StateStrategyClosed extends StateStrategy {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isStateTransition(final EventCountCircuitBreaker breaker,
|
||||
final CheckIntervalData currentData, final CheckIntervalData nextData) {
|
||||
return nextData.getEventCount() > breaker.getOpeningThreshold();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
|
||||
return breaker.getOpeningInterval();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A specialized {@code StateStrategy} implementation for the state open.
|
||||
*/
|
||||
private static class StateStrategyOpen extends StateStrategy {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isStateTransition(final EventCountCircuitBreaker breaker,
|
||||
final CheckIntervalData currentData, final CheckIntervalData nextData) {
|
||||
return nextData.getCheckIntervalStart() != currentData
|
||||
.getCheckIntervalStart()
|
||||
&& currentData.getEventCount() < breaker.getClosingThreshold();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
|
||||
return breaker.getClosingInterval();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class provides a generic implementation of the lazy initialization
|
||||
* pattern.
|
||||
* </p>
|
||||
* <p>
|
||||
* Sometimes an application has to deal with an object only under certain
|
||||
* circumstances, e.g. when the user selects a specific menu item or if a
|
||||
* special event is received. If the creation of the object is costly or the
|
||||
* consumption of memory or other system resources is significant, it may make
|
||||
* sense to defer the creation of this object until it is really needed. This is
|
||||
* a use case for the lazy initialization pattern.
|
||||
* </p>
|
||||
* <p>
|
||||
* This abstract base class provides an implementation of the double-check idiom
|
||||
* for an instance field as discussed in Joshua Bloch's "Effective Java", 2nd
|
||||
* edition, item 71. The class already implements all necessary synchronization.
|
||||
* A concrete subclass has to implement the {@code initialize()} method, which
|
||||
* actually creates the wrapped data object.
|
||||
* </p>
|
||||
* <p>
|
||||
* As an usage example consider that we have a class {@code ComplexObject} whose
|
||||
* instantiation is a complex operation. In order to apply lazy initialization
|
||||
* to this class, a subclass of {@code LazyInitializer} has to be created:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class ComplexObjectInitializer extends LazyInitializer<ComplexObject> {
|
||||
* @Override
|
||||
* protected ComplexObject initialize() {
|
||||
* return new ComplexObject();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Access to the data object is provided through the {@code get()} method. So,
|
||||
* code that wants to obtain the {@code ComplexObject} instance would simply
|
||||
* look like this:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* // Create an instance of the lazy initializer
|
||||
* ComplexObjectInitializer initializer = new ComplexObjectInitializer();
|
||||
* ...
|
||||
* // When the object is actually needed:
|
||||
* ComplexObject cobj = initializer.get();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* If multiple threads call the {@code get()} method when the object has not yet
|
||||
* been created, they are blocked until initialization completes. The algorithm
|
||||
* guarantees that only a single instance of the wrapped object class is
|
||||
* created, which is passed to all callers. Once initialized, calls to the
|
||||
* {@code get()} method are pretty fast because no synchronization is needed
|
||||
* (only an access to a <b>volatile</b> member field).
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the type of the object managed by this initializer class
|
||||
*/
|
||||
public abstract class LazyInitializer<T> implements ConcurrentInitializer<T> {
|
||||
|
||||
private static final Object NO_INIT = new Object();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
// Stores the managed object.
|
||||
private volatile T object = (T) NO_INIT;
|
||||
|
||||
/**
|
||||
* Returns the object wrapped by this instance. On first access the object
|
||||
* is created. After that it is cached and can be accessed pretty fast.
|
||||
*
|
||||
* @return the object initialized by this {@code LazyInitializer}
|
||||
* @throws ConcurrentException if an error occurred during initialization of
|
||||
* the object
|
||||
*/
|
||||
@Override
|
||||
public T get() throws ConcurrentException {
|
||||
// use a temporary variable to reduce the number of reads of the
|
||||
// volatile field
|
||||
T result = object;
|
||||
|
||||
if (result == NO_INIT) {
|
||||
synchronized (this) {
|
||||
result = object;
|
||||
if (result == NO_INIT) {
|
||||
object = result = initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the object managed by this {@code
|
||||
* LazyInitializer}. This method is called by {@link #get()} when the object
|
||||
* is accessed for the first time. An implementation can focus on the
|
||||
* creation of the object. No synchronization is needed, as this is already
|
||||
* handled by {@code get()}.
|
||||
*
|
||||
* @return the managed data object
|
||||
* @throws ConcurrentException if an error occurs during object creation
|
||||
*/
|
||||
protected abstract T initialize() throws ConcurrentException;
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Definition of an interface for a wrapper around a calculation that takes a
|
||||
* single parameter and returns a result. The results for the calculation will
|
||||
* be cached for future requests.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is not a fully functional cache, there is no way of limiting or removing
|
||||
* results once they have been generated. However, it is possible to get the
|
||||
* implementation to regenerate the result for a given parameter, if an error
|
||||
* was thrown during the previous calculation, by setting the option during the
|
||||
* construction of the class. If this is not set the class will return the
|
||||
* cached exception.
|
||||
* </p>
|
||||
* <p>
|
||||
* Thanks should go to Brian Goetz, Tim Peierls and the members of JCP JSR-166
|
||||
* Expert Group for coming up with the original implementation of the class. It
|
||||
* was also published within Java Concurrency in Practice as a sample.
|
||||
* </p>
|
||||
*
|
||||
* @param <I>
|
||||
* the type of the input to the calculation
|
||||
* @param <O>
|
||||
* the type of the output of the calculation
|
||||
*
|
||||
* @since 3.6
|
||||
*/
|
||||
public class Memoizer<I, O> implements Computable<I, O> {
|
||||
|
||||
private final ConcurrentMap<I, Future<O>> cache = new ConcurrentHashMap<>();
|
||||
private final Computable<I, O> computable;
|
||||
private final boolean recalculate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructs a Memoizer for the provided Computable calculation.
|
||||
* </p>
|
||||
* <p>
|
||||
* If a calculation is thrown an exception for any reason, this exception
|
||||
* will be cached and returned for all future calls with the provided
|
||||
* parameter.
|
||||
* </p>
|
||||
*
|
||||
* @param computable
|
||||
* the computation whose results should be memorized
|
||||
*/
|
||||
public Memoizer(final Computable<I, O> computable) {
|
||||
this(computable, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructs a Memoizer for the provided Computable calculation, with the
|
||||
* option of whether a Computation that experiences an error should
|
||||
* recalculate on subsequent calls or return the same cached exception.
|
||||
* </p>
|
||||
*
|
||||
* @param computable
|
||||
* the computation whose results should be memorized
|
||||
* @param recalculate
|
||||
* determines whether the computation should be recalculated on
|
||||
* subsequent calls if the previous call failed
|
||||
*/
|
||||
public Memoizer(final Computable<I, O> computable, final boolean recalculate) {
|
||||
this.computable = computable;
|
||||
this.recalculate = recalculate;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method will return the result of the calculation and cache it, if it
|
||||
* has not previously been calculated.
|
||||
* </p>
|
||||
* <p>
|
||||
* This cache will also cache exceptions that occur during the computation
|
||||
* if the {@code recalculate} parameter is the constructor was set to
|
||||
* {@code false}, or not set. Otherwise, if an exception happened on the
|
||||
* previous calculation, the method will attempt again to generate a value.
|
||||
* </p>
|
||||
*
|
||||
* @param arg
|
||||
* the argument for the calculation
|
||||
* @return the result of the calculation
|
||||
* @throws InterruptedException
|
||||
* thrown if the calculation is interrupted
|
||||
*/
|
||||
@Override
|
||||
public O compute(final I arg) throws InterruptedException {
|
||||
while (true) {
|
||||
Future<O> future = cache.get(arg);
|
||||
if (future == null) {
|
||||
final Callable<O> eval = () -> computable.compute(arg);
|
||||
final FutureTask<O> futureTask = new FutureTask<>(eval);
|
||||
future = cache.putIfAbsent(arg, futureTask);
|
||||
if (future == null) {
|
||||
future = futureTask;
|
||||
futureTask.run();
|
||||
}
|
||||
}
|
||||
try {
|
||||
return future.get();
|
||||
} catch (final CancellationException e) {
|
||||
cache.remove(arg, future);
|
||||
} catch (final ExecutionException e) {
|
||||
if (recalculate) {
|
||||
cache.remove(arg, future);
|
||||
}
|
||||
|
||||
throw launderException(e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method launders a Throwable to either a RuntimeException, Error or
|
||||
* any other Exception wrapped in an IllegalStateException.
|
||||
* </p>
|
||||
*
|
||||
* @param throwable
|
||||
* the throwable to laundered
|
||||
* @return a RuntimeException, Error or an IllegalStateException
|
||||
*/
|
||||
private RuntimeException launderException(final Throwable throwable) {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return (RuntimeException) throwable;
|
||||
} else if (throwable instanceof Error) {
|
||||
throw (Error) throwable;
|
||||
} else {
|
||||
throw new IllegalStateException("Unchecked exception", throwable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A specialized {@link BackgroundInitializer} implementation that can deal with
|
||||
* multiple background initialization tasks.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class has a similar purpose as {@link BackgroundInitializer}. However,
|
||||
* it is not limited to a single background initialization task. Rather it
|
||||
* manages an arbitrary number of {@code BackgroundInitializer} objects,
|
||||
* executes them, and waits until they are completely initialized. This is
|
||||
* useful for applications that have to perform multiple initialization tasks
|
||||
* that can run in parallel (i.e. that do not depend on each other). This class
|
||||
* takes care about the management of an {@code ExecutorService} and shares it
|
||||
* with the {@code BackgroundInitializer} objects it is responsible for; so the
|
||||
* using application need not bother with these details.
|
||||
* </p>
|
||||
* <p>
|
||||
* The typical usage scenario for {@code MultiBackgroundInitializer} is as
|
||||
* follows:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Create a new instance of the class. Optionally pass in a pre-configured
|
||||
* {@code ExecutorService}. Alternatively {@code MultiBackgroundInitializer} can
|
||||
* create a temporary {@code ExecutorService} and delete it after initialization
|
||||
* is complete.</li>
|
||||
* <li>Create specialized {@link BackgroundInitializer} objects for the
|
||||
* initialization tasks to be performed and add them to the {@code
|
||||
* MultiBackgroundInitializer} using the
|
||||
* {@link #addInitializer(String, BackgroundInitializer)} method.</li>
|
||||
* <li>After all initializers have been added, call the {@link #start()} method.
|
||||
* </li>
|
||||
* <li>When access to the result objects produced by the {@code
|
||||
* BackgroundInitializer} objects is needed call the {@link #get()} method. The
|
||||
* object returned here provides access to all result objects created during
|
||||
* initialization. It also stores information about exceptions that have
|
||||
* occurred.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* {@code MultiBackgroundInitializer} starts a special controller task that
|
||||
* starts all {@code BackgroundInitializer} objects added to the instance.
|
||||
* Before the an initializer is started it is checked whether this initializer
|
||||
* already has an {@code ExecutorService} set. If this is the case, this {@code
|
||||
* ExecutorService} is used for running the background task. Otherwise the
|
||||
* current {@code ExecutorService} of this {@code MultiBackgroundInitializer} is
|
||||
* shared with the initializer.
|
||||
* </p>
|
||||
* <p>
|
||||
* The easiest way of using this class is to let it deal with the management of
|
||||
* an {@code ExecutorService} itself: If no external {@code ExecutorService} is
|
||||
* provided, the class creates a temporary {@code ExecutorService} (that is
|
||||
* capable of executing all background tasks in parallel) and destroys it at the
|
||||
* end of background processing.
|
||||
* </p>
|
||||
* <p>
|
||||
* Alternatively an external {@code ExecutorService} can be provided - either at
|
||||
* construction time or later by calling the
|
||||
* {@link #setExternalExecutor(ExecutorService)} method. In this case all
|
||||
* background tasks are scheduled at this external {@code ExecutorService}.
|
||||
* <strong>Important note:</strong> When using an external {@code
|
||||
* ExecutorService} be sure that the number of threads managed by the service is
|
||||
* large enough. Otherwise a deadlock can happen! This is the case in the
|
||||
* following scenario: {@code MultiBackgroundInitializer} starts a task that
|
||||
* starts all registered {@code BackgroundInitializer} objects and waits for
|
||||
* their completion. If for instance a single threaded {@code ExecutorService}
|
||||
* is used, none of the background tasks can be executed, and the task created
|
||||
* by {@code MultiBackgroundInitializer} waits forever.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MultiBackgroundInitializer
|
||||
extends
|
||||
BackgroundInitializer<MultiBackgroundInitializer.MultiBackgroundInitializerResults> {
|
||||
/** A map with the child initializers. */
|
||||
private final Map<String, BackgroundInitializer<?>> childInitializers =
|
||||
new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code MultiBackgroundInitializer}.
|
||||
*/
|
||||
public MultiBackgroundInitializer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code MultiBackgroundInitializer} and
|
||||
* initializes it with the given external {@code ExecutorService}.
|
||||
*
|
||||
* @param exec the {@code ExecutorService} for executing the background
|
||||
* tasks
|
||||
*/
|
||||
public MultiBackgroundInitializer(final ExecutorService exec) {
|
||||
super(exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new {@code BackgroundInitializer} to this object. When this
|
||||
* {@code MultiBackgroundInitializer} is started, the given initializer will
|
||||
* be processed. This method must not be called after {@link #start()} has
|
||||
* been invoked.
|
||||
*
|
||||
* @param name the name of the initializer (must not be <b>null</b>)
|
||||
* @param backgroundInitializer the {@code BackgroundInitializer} to add (must not be
|
||||
* <b>null</b>)
|
||||
* @throws IllegalArgumentException if a required parameter is missing
|
||||
* @throws IllegalStateException if {@code start()} has already been called
|
||||
*/
|
||||
public void addInitializer(final String name, final BackgroundInitializer<?> backgroundInitializer) {
|
||||
Validate.notNull(name, "name");
|
||||
Validate.notNull(backgroundInitializer, "backgroundInitializer");
|
||||
|
||||
synchronized (this) {
|
||||
if (isStarted()) {
|
||||
throw new IllegalStateException("addInitializer() must not be called after start()!");
|
||||
}
|
||||
childInitializers.put(name, backgroundInitializer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tasks needed for executing all child {@code
|
||||
* BackgroundInitializer} objects in parallel. This implementation sums up
|
||||
* the required tasks for all child initializers (which is necessary if one
|
||||
* of the child initializers is itself a {@code MultiBackgroundInitializer}
|
||||
* ). Then it adds 1 for the control task that waits for the completion of
|
||||
* the children.
|
||||
*
|
||||
* @return the number of tasks required for background processing
|
||||
*/
|
||||
@Override
|
||||
protected int getTaskCount() {
|
||||
int result = 1;
|
||||
|
||||
for (final BackgroundInitializer<?> bi : childInitializers.values()) {
|
||||
result += bi.getTaskCount();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the results object. This implementation starts all child {@code
|
||||
* BackgroundInitializer} objects. Then it collects their results and
|
||||
* creates a {@code MultiBackgroundInitializerResults} object with this
|
||||
* data. If a child initializer throws a checked exceptions, it is added to
|
||||
* the results object. Unchecked exceptions are propagated.
|
||||
*
|
||||
* @return the results object
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
@Override
|
||||
protected MultiBackgroundInitializerResults initialize() throws Exception {
|
||||
final Map<String, BackgroundInitializer<?>> inits;
|
||||
synchronized (this) {
|
||||
// create a snapshot to operate on
|
||||
inits = new HashMap<>(
|
||||
childInitializers);
|
||||
}
|
||||
|
||||
// start the child initializers
|
||||
final ExecutorService exec = getActiveExecutor();
|
||||
for (final BackgroundInitializer<?> bi : inits.values()) {
|
||||
if (bi.getExternalExecutor() == null) {
|
||||
// share the executor service if necessary
|
||||
bi.setExternalExecutor(exec);
|
||||
}
|
||||
bi.start();
|
||||
}
|
||||
|
||||
// collect the results
|
||||
final Map<String, Object> results = new HashMap<>();
|
||||
final Map<String, ConcurrentException> excepts = new HashMap<>();
|
||||
for (final Map.Entry<String, BackgroundInitializer<?>> e : inits.entrySet()) {
|
||||
try {
|
||||
results.put(e.getKey(), e.getValue().get());
|
||||
} catch (final ConcurrentException cex) {
|
||||
excepts.put(e.getKey(), cex);
|
||||
}
|
||||
}
|
||||
|
||||
return new MultiBackgroundInitializerResults(inits, results, excepts);
|
||||
}
|
||||
|
||||
/**
|
||||
* A data class for storing the results of the background initialization
|
||||
* performed by {@code MultiBackgroundInitializer}. Objects of this inner
|
||||
* class are returned by {@link MultiBackgroundInitializer#initialize()}.
|
||||
* They allow access to all result objects produced by the
|
||||
* {@link BackgroundInitializer} objects managed by the owning instance. It
|
||||
* is also possible to retrieve status information about single
|
||||
* {@link BackgroundInitializer}s, i.e. whether they completed normally or
|
||||
* caused an exception.
|
||||
*/
|
||||
public static class MultiBackgroundInitializerResults {
|
||||
/** A map with the child initializers. */
|
||||
private final Map<String, BackgroundInitializer<?>> initializers;
|
||||
|
||||
/** A map with the result objects. */
|
||||
private final Map<String, Object> resultObjects;
|
||||
|
||||
/** A map with the exceptions. */
|
||||
private final Map<String, ConcurrentException> exceptions;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code MultiBackgroundInitializerResults}
|
||||
* and initializes it with maps for the {@code BackgroundInitializer}
|
||||
* objects, their result objects and the exceptions thrown by them.
|
||||
*
|
||||
* @param inits the {@code BackgroundInitializer} objects
|
||||
* @param results the result objects
|
||||
* @param excepts the exceptions
|
||||
*/
|
||||
private MultiBackgroundInitializerResults(
|
||||
final Map<String, BackgroundInitializer<?>> inits,
|
||||
final Map<String, Object> results,
|
||||
final Map<String, ConcurrentException> excepts) {
|
||||
initializers = inits;
|
||||
resultObjects = results;
|
||||
exceptions = excepts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code BackgroundInitializer} with the given name. If the
|
||||
* name cannot be resolved, an exception is thrown.
|
||||
*
|
||||
* @param name the name of the {@code BackgroundInitializer}
|
||||
* @return the {@code BackgroundInitializer} with this name
|
||||
* @throws NoSuchElementException if the name cannot be resolved
|
||||
*/
|
||||
public BackgroundInitializer<?> getInitializer(final String name) {
|
||||
return checkName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result object produced by the {@code
|
||||
* BackgroundInitializer} with the given name. This is the object
|
||||
* returned by the initializer's {@code initialize()} method. If this
|
||||
* {@code BackgroundInitializer} caused an exception, <b>null</b> is
|
||||
* returned. If the name cannot be resolved, an exception is thrown.
|
||||
*
|
||||
* @param name the name of the {@code BackgroundInitializer}
|
||||
* @return the result object produced by this {@code
|
||||
* BackgroundInitializer}
|
||||
* @throws NoSuchElementException if the name cannot be resolved
|
||||
*/
|
||||
public Object getResultObject(final String name) {
|
||||
checkName(name);
|
||||
return resultObjects.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag whether the {@code BackgroundInitializer} with the
|
||||
* given name caused an exception.
|
||||
*
|
||||
* @param name the name of the {@code BackgroundInitializer}
|
||||
* @return a flag whether this initializer caused an exception
|
||||
* @throws NoSuchElementException if the name cannot be resolved
|
||||
*/
|
||||
public boolean isException(final String name) {
|
||||
checkName(name);
|
||||
return exceptions.containsKey(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code ConcurrentException} object that was thrown by the
|
||||
* {@code BackgroundInitializer} with the given name. If this
|
||||
* initializer did not throw an exception, the return value is
|
||||
* <b>null</b>. If the name cannot be resolved, an exception is thrown.
|
||||
*
|
||||
* @param name the name of the {@code BackgroundInitializer}
|
||||
* @return the exception thrown by this initializer
|
||||
* @throws NoSuchElementException if the name cannot be resolved
|
||||
*/
|
||||
public ConcurrentException getException(final String name) {
|
||||
checkName(name);
|
||||
return exceptions.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set with the names of all {@code BackgroundInitializer}
|
||||
* objects managed by the {@code MultiBackgroundInitializer}.
|
||||
*
|
||||
* @return an (unmodifiable) set with the names of the managed {@code
|
||||
* BackgroundInitializer} objects
|
||||
*/
|
||||
public Set<String> initializerNames() {
|
||||
return Collections.unmodifiableSet(initializers.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag whether the whole initialization was successful. This
|
||||
* is the case if no child initializer has thrown an exception.
|
||||
*
|
||||
* @return a flag whether the initialization was successful
|
||||
*/
|
||||
public boolean isSuccessful() {
|
||||
return exceptions.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an initializer with the given name exists. If not,
|
||||
* throws an exception. If it exists, the associated child initializer
|
||||
* is returned.
|
||||
*
|
||||
* @param name the name to check
|
||||
* @return the initializer with this name
|
||||
* @throws NoSuchElementException if the name is unknown
|
||||
*/
|
||||
private BackgroundInitializer<?> checkName(final String name) {
|
||||
final BackgroundInitializer<?> init = initializers.get(name);
|
||||
if (init == null) {
|
||||
throw new NoSuchElementException(
|
||||
"No child initializer with name " + name);
|
||||
}
|
||||
|
||||
return init;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A simple implementation of the <a
|
||||
* href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern
|
||||
* that opens if the requested increment amount is greater than a given threshold.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It contains an internal counter that starts in zero, and each call increments the counter by a given amount.
|
||||
* If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An example of use case could be a memory circuit breaker.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* long threshold = 10L;
|
||||
* ThresholdCircuitBreaker breaker = new ThresholdCircuitBreaker(10L);
|
||||
* ...
|
||||
* public void handleRequest(Request request) {
|
||||
* long memoryUsed = estimateMemoryUsage(request);
|
||||
* if (breaker.incrementAndCheckState(memoryUsed)) {
|
||||
* // actually handle this request
|
||||
* } else {
|
||||
* // do something else, e.g. send an error code
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>#Thread safe#</p>
|
||||
* @since 3.5
|
||||
*/
|
||||
public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> {
|
||||
/**
|
||||
* The initial value of the internal counter.
|
||||
*/
|
||||
private static final long INITIAL_COUNT = 0L;
|
||||
|
||||
/**
|
||||
* The threshold.
|
||||
*/
|
||||
private final long threshold;
|
||||
|
||||
/**
|
||||
* Controls the amount used.
|
||||
*/
|
||||
private final AtomicLong used;
|
||||
|
||||
/**
|
||||
* <p>Creates a new instance of {@code ThresholdCircuitBreaker} and initializes the threshold.</p>
|
||||
*
|
||||
* @param threshold the threshold.
|
||||
*/
|
||||
public ThresholdCircuitBreaker(final long threshold) {
|
||||
this.used = new AtomicLong(INITIAL_COUNT);
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the threshold.
|
||||
*
|
||||
* @return the threshold
|
||||
*/
|
||||
public long getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean checkState() {
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>Resets the internal counter back to its initial value (zero).</p>
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
this.used.set(INITIAL_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state.</p>
|
||||
*/
|
||||
@Override
|
||||
public boolean incrementAndCheckState(final Long increment) {
|
||||
if (threshold == 0) {
|
||||
open();
|
||||
}
|
||||
|
||||
final long used = this.used.addAndGet(increment);
|
||||
if (used > threshold) {
|
||||
open();
|
||||
}
|
||||
|
||||
return checkState();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A specialized <em>semaphore</em> implementation that provides a number of
|
||||
* permits in a given time frame.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class is similar to the {@code java.util.concurrent.Semaphore} class
|
||||
* provided by the JDK in that it manages a configurable number of permits.
|
||||
* Using the {@link #acquire()} method a permit can be requested by a thread.
|
||||
* However, there is an additional timing dimension: there is no {@code
|
||||
* release()} method for freeing a permit, but all permits are automatically
|
||||
* released at the end of a configurable time frame. If a thread calls
|
||||
* {@link #acquire()} and the available permits are already exhausted for this
|
||||
* time frame, the thread is blocked. When the time frame ends all permits
|
||||
* requested so far are restored, and blocking threads are waked up again, so
|
||||
* that they can try to acquire a new permit. This basically means that in the
|
||||
* specified time frame only the given number of operations is possible.
|
||||
* </p>
|
||||
* <p>
|
||||
* A use case for this class is to artificially limit the load produced by a
|
||||
* process. As an example consider an application that issues database queries
|
||||
* on a production system in a background process to gather statistical
|
||||
* information. This background processing should not produce so much database
|
||||
* load that the functionality and the performance of the production system are
|
||||
* impacted. Here a {@code TimedSemaphore} could be installed to guarantee that
|
||||
* only a given number of database queries are issued per second.
|
||||
* </p>
|
||||
* <p>
|
||||
* A thread class for performing database queries could look as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class StatisticsThread extends Thread {
|
||||
* // The semaphore for limiting database load.
|
||||
* private final TimedSemaphore semaphore;
|
||||
* // Create an instance and set the semaphore
|
||||
* public StatisticsThread(TimedSemaphore timedSemaphore) {
|
||||
* semaphore = timedSemaphore;
|
||||
* }
|
||||
* // Gather statistics
|
||||
* public void run() {
|
||||
* try {
|
||||
* while (true) {
|
||||
* semaphore.acquire(); // limit database load
|
||||
* performQuery(); // issue a query
|
||||
* }
|
||||
* } catch(InterruptedException) {
|
||||
* // fall through
|
||||
* }
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The following code fragment shows how a {@code TimedSemaphore} is created
|
||||
* that allows only 10 operations per second and passed to the statistics
|
||||
* thread:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* TimedSemaphore sem = new TimedSemaphore(1, TimeUnit.SECOND, 10);
|
||||
* StatisticsThread thread = new StatisticsThread(sem);
|
||||
* thread.start();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* When creating an instance the time period for the semaphore must be
|
||||
* specified. {@code TimedSemaphore} uses an executor service with a
|
||||
* corresponding period to monitor this interval. The {@code
|
||||
* ScheduledExecutorService} to be used for this purpose can be provided at
|
||||
* construction time. Alternatively the class creates an internal executor
|
||||
* service.
|
||||
* </p>
|
||||
* <p>
|
||||
* Client code that uses {@code TimedSemaphore} has to call the
|
||||
* {@link #acquire()} method in each processing step. {@code TimedSemaphore}
|
||||
* keeps track of the number of invocations of the {@link #acquire()} method and
|
||||
* blocks the calling thread if the counter exceeds the limit specified. When
|
||||
* the timer signals the end of the time period the counter is reset and all
|
||||
* waiting threads are released. Then another cycle can start.
|
||||
* </p>
|
||||
* <p>
|
||||
* An alternative to {@code acquire()} is the {@link #tryAcquire()} method. This
|
||||
* method checks whether the semaphore is under the specified limit and
|
||||
* increases the internal counter if this is the case. The return value is then
|
||||
* <strong>true</strong>, and the calling thread can continue with its action.
|
||||
* If the semaphore is already at its limit, {@code tryAcquire()} immediately
|
||||
* returns <strong>false</strong> without blocking; the calling thread must
|
||||
* then abort its action. This usage scenario prevents blocking of threads.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is possible to modify the limit at any time using the
|
||||
* {@link #setLimit(int)} method. This is useful if the load produced by an
|
||||
* operation has to be adapted dynamically. In the example scenario with the
|
||||
* thread collecting statistics it may make sense to specify a low limit during
|
||||
* day time while allowing a higher load in the night time. Reducing the limit
|
||||
* takes effect immediately by blocking incoming callers. If the limit is
|
||||
* increased, waiting threads are not released immediately, but wake up when the
|
||||
* timer runs out. Then, in the next period more processing steps can be
|
||||
* performed without blocking. By setting the limit to 0 the semaphore can be
|
||||
* switched off: in this mode the {@link #acquire()} method never blocks, but
|
||||
* lets all callers pass directly.
|
||||
* </p>
|
||||
* <p>
|
||||
* When the {@code TimedSemaphore} is no more needed its {@link #shutdown()}
|
||||
* method should be called. This causes the periodic task that monitors the time
|
||||
* interval to be canceled. If the {@code ScheduledExecutorService} has been
|
||||
* created by the semaphore at construction time, it is also shut down.
|
||||
* resources. After that {@link #acquire()} must not be called any more.
|
||||
* </p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class TimedSemaphore {
|
||||
/**
|
||||
* Constant for a value representing no limit. If the limit is set to a
|
||||
* value less or equal this constant, the {@code TimedSemaphore} will be
|
||||
* effectively switched off.
|
||||
*/
|
||||
public static final int NO_LIMIT = 0;
|
||||
|
||||
/** Constant for the thread pool size for the executor. */
|
||||
private static final int THREAD_POOL_SIZE = 1;
|
||||
|
||||
/** The executor service for managing the timer thread. */
|
||||
private final ScheduledExecutorService executorService;
|
||||
|
||||
/** Stores the period for this timed semaphore. */
|
||||
private final long period;
|
||||
|
||||
/** The time unit for the period. */
|
||||
private final TimeUnit unit;
|
||||
|
||||
/** A flag whether the executor service was created by this object. */
|
||||
private final boolean ownExecutor;
|
||||
|
||||
/** A future object representing the timer task. */
|
||||
private ScheduledFuture<?> task; // @GuardedBy("this")
|
||||
|
||||
/** Stores the total number of invocations of the acquire() method. */
|
||||
private long totalAcquireCount; // @GuardedBy("this")
|
||||
|
||||
/**
|
||||
* The counter for the periods. This counter is increased every time a
|
||||
* period ends.
|
||||
*/
|
||||
private long periodCount; // @GuardedBy("this")
|
||||
|
||||
/** The limit. */
|
||||
private int limit; // @GuardedBy("this")
|
||||
|
||||
/** The current counter. */
|
||||
private int acquireCount; // @GuardedBy("this")
|
||||
|
||||
/** The number of invocations of acquire() in the last period. */
|
||||
private int lastCallsPerPeriod; // @GuardedBy("this")
|
||||
|
||||
/** A flag whether shutdown() was called. */
|
||||
private boolean shutdown; // @GuardedBy("this")
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link TimedSemaphore} and initializes it with
|
||||
* the given time period and the limit.
|
||||
*
|
||||
* @param timePeriod the time period
|
||||
* @param timeUnit the unit for the period
|
||||
* @param limit the limit for the semaphore
|
||||
* @throws IllegalArgumentException if the period is less or equals 0
|
||||
*/
|
||||
public TimedSemaphore(final long timePeriod, final TimeUnit timeUnit, final int limit) {
|
||||
this(null, timePeriod, timeUnit, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link TimedSemaphore} and initializes it with
|
||||
* an executor service, the given time period, and the limit. The executor
|
||||
* service will be used for creating a periodic task for monitoring the time
|
||||
* period. It can be <b>null</b>, then a default service will be created.
|
||||
*
|
||||
* @param service the executor service
|
||||
* @param timePeriod the time period
|
||||
* @param timeUnit the unit for the period
|
||||
* @param limit the limit for the semaphore
|
||||
* @throws IllegalArgumentException if the period is less or equals 0
|
||||
*/
|
||||
public TimedSemaphore(final ScheduledExecutorService service, final long timePeriod,
|
||||
final TimeUnit timeUnit, final int limit) {
|
||||
Validate.inclusiveBetween(1, Long.MAX_VALUE, timePeriod, "Time period must be greater than 0!");
|
||||
|
||||
period = timePeriod;
|
||||
unit = timeUnit;
|
||||
|
||||
if (service != null) {
|
||||
executorService = service;
|
||||
ownExecutor = false;
|
||||
} else {
|
||||
final ScheduledThreadPoolExecutor s = new ScheduledThreadPoolExecutor(
|
||||
THREAD_POOL_SIZE);
|
||||
s.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
s.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
executorService = s;
|
||||
ownExecutor = true;
|
||||
}
|
||||
|
||||
setLimit(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the limit enforced by this semaphore. The limit determines how
|
||||
* many invocations of {@link #acquire()} are allowed within the monitored
|
||||
* period.
|
||||
*
|
||||
* @return the limit
|
||||
*/
|
||||
public final synchronized int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the limit. This is the number of times the {@link #acquire()} method
|
||||
* can be called within the time period specified. If this limit is reached,
|
||||
* further invocations of {@link #acquire()} will block. Setting the limit
|
||||
* to a value <= {@link #NO_LIMIT} will cause the limit to be disabled,
|
||||
* i.e. an arbitrary number of{@link #acquire()} invocations is allowed in
|
||||
* the time period.
|
||||
*
|
||||
* @param limit the limit
|
||||
*/
|
||||
public final synchronized void setLimit(final int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a shutdown. After that the object cannot be used any more.
|
||||
* This method can be invoked an arbitrary number of times. All invocations
|
||||
* after the first one do not have any effect.
|
||||
*/
|
||||
public synchronized void shutdown() {
|
||||
if (!shutdown) {
|
||||
|
||||
if (ownExecutor) {
|
||||
// if the executor was created by this instance, it has
|
||||
// to be shutdown
|
||||
getExecutorService().shutdownNow();
|
||||
}
|
||||
if (task != null) {
|
||||
task.cancel(false);
|
||||
}
|
||||
|
||||
shutdown = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the {@link #shutdown()} method has been called on this
|
||||
* object. If this method returns <b>true</b>, this instance cannot be used
|
||||
* any longer.
|
||||
*
|
||||
* @return a flag whether a shutdown has been performed
|
||||
*/
|
||||
public synchronized boolean isShutdown() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a permit from this semaphore. This method will block if
|
||||
* the limit for the current period has already been reached. If
|
||||
* {@link #shutdown()} has already been invoked, calling this method will
|
||||
* cause an exception. The very first call of this method starts the timer
|
||||
* task which monitors the time period set for this {@code TimedSemaphore}.
|
||||
* From now on the semaphore is active.
|
||||
*
|
||||
* @throws InterruptedException if the thread gets interrupted
|
||||
* @throws IllegalStateException if this semaphore is already shut down
|
||||
*/
|
||||
public synchronized void acquire() throws InterruptedException {
|
||||
prepareAcquire();
|
||||
|
||||
boolean canPass;
|
||||
do {
|
||||
canPass = acquirePermit();
|
||||
if (!canPass) {
|
||||
wait();
|
||||
}
|
||||
} while (!canPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to acquire a permit from this semaphore. If the limit of this semaphore has
|
||||
* not yet been reached, a permit is acquired, and this method returns
|
||||
* <strong>true</strong>. Otherwise, this method returns immediately with the result
|
||||
* <strong>false</strong>.
|
||||
*
|
||||
* @return <strong>true</strong> if a permit could be acquired; <strong>false</strong>
|
||||
* otherwise
|
||||
* @throws IllegalStateException if this semaphore is already shut down
|
||||
* @since 3.5
|
||||
*/
|
||||
public synchronized boolean tryAcquire() {
|
||||
prepareAcquire();
|
||||
return acquirePermit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of (successful) acquire invocations during the last
|
||||
* period. This is the number of times the {@link #acquire()} method was
|
||||
* called without blocking. This can be useful for testing or debugging
|
||||
* purposes or to determine a meaningful threshold value. If a limit is set,
|
||||
* the value returned by this method won't be greater than this limit.
|
||||
*
|
||||
* @return the number of non-blocking invocations of the {@link #acquire()}
|
||||
* method
|
||||
*/
|
||||
public synchronized int getLastAcquiresPerPeriod() {
|
||||
return lastCallsPerPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of invocations of the {@link #acquire()} method for
|
||||
* the current period. This may be useful for testing or debugging purposes.
|
||||
*
|
||||
* @return the current number of {@link #acquire()} invocations
|
||||
*/
|
||||
public synchronized int getAcquireCount() {
|
||||
return acquireCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of calls to the {@link #acquire()} method that can
|
||||
* still be performed in the current period without blocking. This method
|
||||
* can give an indication whether it is safe to call the {@link #acquire()}
|
||||
* method without risking to be suspended. However, there is no guarantee
|
||||
* that a subsequent call to {@link #acquire()} actually is not-blocking
|
||||
* because in the mean time other threads may have invoked the semaphore.
|
||||
*
|
||||
* @return the current number of available {@link #acquire()} calls in the
|
||||
* current period
|
||||
*/
|
||||
public synchronized int getAvailablePermits() {
|
||||
return getLimit() - getAcquireCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the average number of successful (i.e. non-blocking)
|
||||
* {@link #acquire()} invocations for the entire life-time of this {@code
|
||||
* TimedSemaphore}. This method can be used for instance for statistical
|
||||
* calculations.
|
||||
*
|
||||
* @return the average number of {@link #acquire()} invocations per time
|
||||
* unit
|
||||
*/
|
||||
public synchronized double getAverageCallsPerPeriod() {
|
||||
return periodCount == 0 ? 0 : (double) totalAcquireCount
|
||||
/ (double) periodCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time period. This is the time monitored by this semaphore.
|
||||
* Only a given number of invocations of the {@link #acquire()} method is
|
||||
* possible in this period.
|
||||
*
|
||||
* @return the time period
|
||||
*/
|
||||
public long getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time unit. This is the unit used by {@link #getPeriod()}.
|
||||
*
|
||||
* @return the time unit
|
||||
*/
|
||||
public TimeUnit getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the executor service used by this instance.
|
||||
*
|
||||
* @return the executor service
|
||||
*/
|
||||
protected ScheduledExecutorService getExecutorService() {
|
||||
return executorService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the timer. This method is called when {@link #acquire()} is called
|
||||
* for the first time. It schedules a task to be executed at fixed rate to
|
||||
* monitor the time period specified.
|
||||
*
|
||||
* @return a future object representing the task scheduled
|
||||
*/
|
||||
protected ScheduledFuture<?> startTimer() {
|
||||
return getExecutorService().scheduleAtFixedRate(this::endOfPeriod, getPeriod(), getPeriod(), getUnit());
|
||||
}
|
||||
|
||||
/**
|
||||
* The current time period is finished. This method is called by the timer
|
||||
* used internally to monitor the time period. It resets the counter and
|
||||
* releases the threads waiting for this barrier.
|
||||
*/
|
||||
synchronized void endOfPeriod() {
|
||||
lastCallsPerPeriod = acquireCount;
|
||||
totalAcquireCount += acquireCount;
|
||||
periodCount++;
|
||||
acquireCount = 0;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an acquire operation. Checks for the current state and starts the internal
|
||||
* timer if necessary. This method must be called with the lock of this object held.
|
||||
*/
|
||||
private void prepareAcquire() {
|
||||
if (isShutdown()) {
|
||||
throw new IllegalStateException("TimedSemaphore is shut down!");
|
||||
}
|
||||
|
||||
if (task == null) {
|
||||
task = startTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper method for acquiring a permit. This method checks whether currently
|
||||
* a permit can be acquired and - if so - increases the internal counter. The return
|
||||
* value indicates whether a permit could be acquired. This method must be called with
|
||||
* the lock of this object held.
|
||||
*
|
||||
* @return a flag whether a permit could be acquired
|
||||
*/
|
||||
private boolean acquirePermit() {
|
||||
if (getLimit() <= NO_LIMIT || acquireCount < getLimit()) {
|
||||
acquireCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang3.concurrent.locks;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.function.Failable;
|
||||
import org.apache.commons.lang3.function.FailableConsumer;
|
||||
import org.apache.commons.lang3.function.FailableFunction;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked
|
||||
* objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern
|
||||
* (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern
|
||||
* [Gamma, E., Helm, R., & Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.].
|
||||
* </p>
|
||||
* <p>
|
||||
* Locking is preferable, if there is a distinction between read access (multiple threads may have read access
|
||||
* concurrently), and write access (only one thread may have write access at any given time). In comparison,
|
||||
* synchronization doesn't support read access, because synchronized access is exclusive.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using this class is fairly straightforward:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling
|
||||
* {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the
|
||||
* locked object. Instead, use references to the lock.</li>
|
||||
* <li>If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked
|
||||
* object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke
|
||||
* {@link LockingVisitors.StampedLockVisitor#acceptReadLocked(FailableConsumer)}, or
|
||||
* {@link LockingVisitors.StampedLockVisitor#acceptWriteLocked(FailableConsumer)}, passing the consumer.</li>
|
||||
* <li>As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function
|
||||
* may also be implemented as a Lambda. To have the function executed, invoke
|
||||
* {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or
|
||||
* {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Example: A thread safe logger class.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* public class SimpleLogger {
|
||||
*
|
||||
* private final StampedLockVisitor<PrintStream> lock;
|
||||
*
|
||||
* public SimpleLogger(OutputStream out) {
|
||||
* lock = LockingVisitors.stampedLockVisitor(new PrintStream(out));
|
||||
* }
|
||||
*
|
||||
* public void log(String message) {
|
||||
* lock.acceptWriteLocked((ps) -> ps.println(message));
|
||||
* }
|
||||
*
|
||||
* public void log(byte[] buffer) {
|
||||
* lock.acceptWriteLocked((ps) -> { ps.write(buffer); ps.println(); });
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
public class LockingVisitors {
|
||||
|
||||
/**
|
||||
* Wraps a domain object and a lock for access by lambdas.
|
||||
*
|
||||
* @param <O> the wrapped object type.
|
||||
* @param <L> the wrapped lock type.
|
||||
*/
|
||||
public static class LockVisitor<O, L> {
|
||||
|
||||
/**
|
||||
* The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
|
||||
* Java 8.
|
||||
*/
|
||||
private final L lock;
|
||||
|
||||
/**
|
||||
* The guarded object.
|
||||
*/
|
||||
private final O object;
|
||||
|
||||
/**
|
||||
* Supplies the read lock, usually from the lock object.
|
||||
*/
|
||||
private final Supplier<Lock> readLockSupplier;
|
||||
|
||||
/**
|
||||
* Supplies the write lock, usually from the lock object.
|
||||
*/
|
||||
private final Supplier<Lock> writeLockSupplier;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param object The object to guard.
|
||||
* @param lock The locking object.
|
||||
* @param readLockSupplier Supplies the read lock, usually from the lock object.
|
||||
* @param writeLockSupplier Supplies the write lock, usually from the lock object.
|
||||
*/
|
||||
protected LockVisitor(final O object, final L lock, final Supplier<Lock> readLockSupplier, final Supplier<Lock> writeLockSupplier) {
|
||||
this.object = Objects.requireNonNull(object, "object");
|
||||
this.lock = Objects.requireNonNull(lock, "lock");
|
||||
this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier");
|
||||
this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier");
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
|
||||
* will do (in the given order):
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
|
||||
* lock is granted.</li>
|
||||
* <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
|
||||
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
|
||||
* lock will be released anyways.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
|
||||
* consumers parameter.
|
||||
* @see #acceptWriteLocked(FailableConsumer)
|
||||
* @see #applyReadLocked(FailableFunction)
|
||||
*/
|
||||
public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
|
||||
lockAcceptUnlock(readLockSupplier, consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
|
||||
* the given order):
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
|
||||
* lock is granted.</li>
|
||||
* <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
|
||||
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
|
||||
* lock will be released anyways.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
|
||||
* consumers parameter.
|
||||
* @see #acceptReadLocked(FailableConsumer)
|
||||
* @see #applyWriteLocked(FailableFunction)
|
||||
*/
|
||||
public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
|
||||
lockAcceptUnlock(writeLockSupplier, consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
|
||||
* result object. More precisely, what the method will do (in the given order):
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
|
||||
* lock is granted.</li>
|
||||
* <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
|
||||
* receiving the functions result.</li>
|
||||
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
|
||||
* lock will be released anyways.</li>
|
||||
* <li>Return the result object, that has been received from the functions invocation.</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the
|
||||
* list. This might be achieved with the following:
|
||||
* </p>
|
||||
* <pre>
|
||||
* private Lock<List<Object>> listLock;
|
||||
*
|
||||
* public int getCurrentListSize() {
|
||||
* final Integer sizeInteger = listLock.applyReadLocked((list) -> Integer.valueOf(list.size));
|
||||
* return sizeInteger.intValue();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> The result type (both the functions, and this method's.)
|
||||
* @param function The function, which is being invoked to compute the result. The function will receive the
|
||||
* hidden object.
|
||||
* @return The result object, which has been returned by the functions invocation.
|
||||
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
|
||||
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
|
||||
* @see #acceptReadLocked(FailableConsumer)
|
||||
* @see #applyWriteLocked(FailableFunction)
|
||||
*/
|
||||
public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
|
||||
return lockApplyUnlock(readLockSupplier, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
|
||||
* More precisely, what the method will do (in the given order):
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
|
||||
* lock is granted.</li>
|
||||
* <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
|
||||
* receiving the functions result.</li>
|
||||
* <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
|
||||
* lock will be released anyways.</li>
|
||||
* <li>Return the result object, that has been received from the functions invocation.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param <T> The result type (both the functions, and this method's.)
|
||||
* @param function The function, which is being invoked to compute the result. The function will receive the
|
||||
* hidden object.
|
||||
* @return The result object, which has been returned by the functions invocation.
|
||||
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
|
||||
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
|
||||
* @see #acceptReadLocked(FailableConsumer)
|
||||
* @see #applyWriteLocked(FailableFunction)
|
||||
*/
|
||||
public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
|
||||
return lockApplyUnlock(writeLockSupplier, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lock.
|
||||
*
|
||||
* @return the lock.
|
||||
*/
|
||||
public L getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the guarded object.
|
||||
*
|
||||
* @return the object.
|
||||
*/
|
||||
public O getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and
|
||||
* {@link #acceptWriteLocked(FailableConsumer)}.
|
||||
*
|
||||
* @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
|
||||
* internally.)
|
||||
* @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
|
||||
* as a parameter.
|
||||
* @see #acceptReadLocked(FailableConsumer)
|
||||
* @see #acceptWriteLocked(FailableConsumer)
|
||||
*/
|
||||
protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
|
||||
final Lock lock = lockSupplier.get();
|
||||
lock.lock();
|
||||
try {
|
||||
consumer.accept(object);
|
||||
} catch (final Throwable t) {
|
||||
throw Failable.rethrow(t);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
|
||||
* {@link #applyWriteLocked(FailableFunction)}.
|
||||
*
|
||||
* @param <T> The result type (both the functions, and this method's.)
|
||||
* @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
|
||||
* internally.)
|
||||
* @param function The function, which is being invoked to compute the result object. This function will receive
|
||||
* the locked (hidden) object as a parameter.
|
||||
* @return The result object, which has been returned by the functions invocation.
|
||||
* @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
|
||||
* access to the hidden object beyond this methods lifetime and will therefore be prevented.
|
||||
* @see #applyReadLocked(FailableFunction)
|
||||
* @see #applyWriteLocked(FailableFunction)
|
||||
*/
|
||||
protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
|
||||
final Lock lock = lockSupplier.get();
|
||||
lock.lock();
|
||||
try {
|
||||
return function.apply(object);
|
||||
} catch (final Throwable t) {
|
||||
throw Failable.rethrow(t);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
|
||||
* idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
|
||||
* accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
|
||||
* {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
|
||||
* necessary protections are guaranteed.
|
||||
*
|
||||
* @param <O> The locked (hidden) objects type.
|
||||
*/
|
||||
public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> {
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
|
||||
* only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
|
||||
*
|
||||
* @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
|
||||
* @param readWriteLock the lock to use.
|
||||
*/
|
||||
protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
|
||||
super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
|
||||
* idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
|
||||
* accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
|
||||
* {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
|
||||
* necessary protections are guaranteed.
|
||||
*
|
||||
* @param <O> The locked (hidden) objects type.
|
||||
*/
|
||||
public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> {
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
|
||||
* only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
|
||||
*
|
||||
* @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
|
||||
* @param stampedLock the lock to use.
|
||||
*/
|
||||
protected StampedLockVisitor(final O object, final StampedLock stampedLock) {
|
||||
super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
|
||||
*
|
||||
* @param <O> The locked objects type.
|
||||
* @param object The locked (hidden) object.
|
||||
* @return The created instance, a {@link StampedLockVisitor lock} for the given object.
|
||||
*/
|
||||
public static <O> ReadWriteLockVisitor<O> reentrantReadWriteLockVisitor(final O object) {
|
||||
return new LockingVisitors.ReadWriteLockVisitor<>(object, new ReentrantReadWriteLock());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
|
||||
*
|
||||
* @param <O> The locked objects type.
|
||||
* @param object The locked (hidden) object.
|
||||
* @return The created instance, a {@link StampedLockVisitor lock} for the given object.
|
||||
*/
|
||||
public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
|
||||
return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock());
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue