Imported commons-lang 3.12.0 sources

This commit is contained in:
Claudio Maggioni 2023-04-11 09:06:12 +02:00
commit 3fcefd6e59
475 changed files with 181603 additions and 0 deletions

24
before/.gitattributes vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

17
before/SECURITY.md Normal file
View 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

File diff suppressed because it is too large Load diff

View 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>

View 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>

View 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>

File diff suppressed because it is too large Load diff

View 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

View file

@ -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);
}
}

View 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);
}
}

View 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;
}
}

File diff suppressed because it is too large Load diff

View 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);
}
}

File diff suppressed because it is too large Load diff

View 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;
}
}
}

View 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();
}
}
}

View file

@ -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) &amp;&amp; (<i>k</i> &gt;= 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) &amp;&amp; (<i>k</i> &gt;= 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) &amp;&amp; (<i>k</i> &lt;= 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) &amp;&amp; (<i>k</i> &lt;= 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;
}
}

View 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();
}
}

View 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() {
}
}

View 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('&copy;') = 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('&copy;') = 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('&copy;') = 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('&copy;') = 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('&copy;') = 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('&copy;') = 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('&copy;') = 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('&copy;') = 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;
}
}

View 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;
}
}

View file

@ -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());
}
}

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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() {
}
}

View 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&lt;O&gt;} with a {@link FailableConsumer
* FailableConsumer&lt;O,? extends Throwable&gt;}, 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);
}
}

View 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;
}
}

View 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() {
}
}

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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} &lt; 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} &lt; 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} &lt; 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);
}
}

View 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);
}
}

View 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&lt;Integer&gt; range = Range.between(16, 64);
* range.fit(-9) --&gt; 16
* range.fit(0) --&gt; 16
* range.fit(15) --&gt; 16
* range.fit(16) --&gt; 16
* range.fit(17) --&gt; 17
* ...
* range.fit(63) --&gt; 63
* range.fit(64) --&gt; 64
* range.fit(99) --&gt; 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);
}
}

View 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&lt;__&gt;\n&lt;__&gt;B", Pattern.compile("&lt;.*&gt;")) = "A\nB"
* StringUtils.removeAll("A&lt;__&gt;\n&lt;__&gt;B", Pattern.compile("(?s)&lt;.*&gt;")) = "AB"
* StringUtils.removeAll("A&lt;__&gt;\n&lt;__&gt;B", Pattern.compile("&lt;.*&gt;", 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&lt;__&gt;\n&lt;__&gt;B", "&lt;.*&gt;") = "A\nB"
* StringUtils.removeAll("A&lt;__&gt;\n&lt;__&gt;B", "(?s)&lt;.*&gt;") = "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&lt;__&gt;\n&lt;__&gt;B", Pattern.compile("&lt;.*&gt;")) = "A\n&lt;__&gt;B"
* StringUtils.removeFirst("A&lt;__&gt;\n&lt;__&gt;B", Pattern.compile("(?s)&lt;.*&gt;")) = "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&lt;__&gt;\n&lt;__&gt;B", "&lt;.*&gt;") = "A\n&lt;__&gt;B"
* StringUtils.removeFirst("A&lt;__&gt;\n&lt;__&gt;B", "(?s)&lt;.*&gt;") = "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(&quot;(?s)&quot; + 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&lt;__&gt;\n&lt;__&gt;B", "&lt;.*&gt;") = "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("&lt;__&gt;\n&lt;__&gt;", Pattern.compile("&lt;.*&gt;"), "z") = "z\nz"
* StringUtils.replaceAll("&lt;__&gt;\n&lt;__&gt;", Pattern.compile("&lt;.*&gt;", Pattern.DOTALL), "z") = "z"
* StringUtils.replaceAll("&lt;__&gt;\n&lt;__&gt;", Pattern.compile("(?s)&lt;.*&gt;"), "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("&lt;__&gt;\n&lt;__&gt;", "&lt;.*&gt;", "z") = "z\nz"
* StringUtils.replaceAll("&lt;__&gt;\n&lt;__&gt;", "(?s)&lt;.*&gt;", "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("&lt;__&gt;\n&lt;__&gt;", Pattern.compile("&lt;.*&gt;"), "z") = "z\n&lt;__&gt;"
* StringUtils.replaceFirst("&lt;__&gt;\n&lt;__&gt;", Pattern.compile("(?s)&lt;.*&gt;"), "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("&lt;__&gt;\n&lt;__&gt;", "&lt;.*&gt;", "z") = "z\n&lt;__&gt;"
* StringUtils.replaceFirst("&lt;__&gt;\n&lt;__&gt;", "(?s)&lt;.*&gt;", "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(&quot;(?s)&quot; + 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("&lt;__&gt;\n&lt;__&gt;", "&lt;.*&gt;", "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);
}
}

View file

@ -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);
}
}

View file

@ -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() {
}
}

View 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&lt;java.lang.reflect.Method&gt; consumer = m -&gt; {
* 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) -&gt; 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&lt;O&gt; list;
* final Method m;
* final Function&lt;O,String&gt; mapper = (o) -&gt; {
* try {
* return (String) m.invoke(o);
* } catch (Throwable t) {
* throw Functions.rethrow(t);
* }
* };
* final List&lt;String&gt; strList = list.stream()
* .map(mapper).collect(Collectors.toList());
* </pre>
* as follows:
* <pre>
* final List&lt;O&gt; list;
* final Method m;
* final List&lt;String&gt; strList = Functions.stream(list.stream())
* .map((o) -&gt; (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&lt;O&gt; list;
* final Method m;
* final Function&lt;O,String&gt; mapper = (o) -&gt; {
* try {
* return (String) m.invoke(o);
* } catch (Throwable t) {
* throw Functions.rethrow(t);
* }
* };
* final List&lt;String&gt; strList = list.stream()
* .map(mapper).collect(Collectors.toList());
* </pre>
* as follows:
* <pre>
* final List&lt;O&gt; list;
* final Method m;
* final List&lt;String&gt; strList = Functions.stream(list.stream())
* .map((o) -&gt; (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);
}
}

View file

@ -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", "&#11;" },
{ "\u000c", "&#12;" },
{ "\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" &amp; "butter"}</p>
* becomes:
* <p>
* {@code &amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;}.
* </p>
*
* <p>Supports all known HTML 4.0 entities, including funky accents.
* Note that the commonly used apostrophe escape character (&amp;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 "&lt;Fran&ccedil;ais&gt;"}
* 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 "&gt;&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"} =&gt;
* {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
* </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"} =&gt;
* {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
* </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"} =&gt;
* {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
* </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);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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() {
}
}

File diff suppressed because it is too large Load diff

View file

@ -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,
/**
* AppleIBMMotorola 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;
}
}

View file

@ -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;

View file

@ -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&lt;Font&gt; {
* 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

View 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.");
}
}

View file

@ -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&lt;Person&gt; {
* 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&lt;Person&gt; {
* String name;
* Address address; // implements Diffable&lt;Address&gt;
*
* ...
*
* 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");
}
}

View file

@ -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();
}
}

View file

@ -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

View 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
}

View file

@ -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();
}
}

View 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
}

View file

@ -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;
}
}

View file

@ -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>
* &nbsp; name=Stephen, <br>
* &nbsp; age=29, <br>
* &nbsp; smoker=false, <br>
* &nbsp; job=Job@43cd2[ <br>
* &nbsp; &nbsp; title=Manager <br>
* &nbsp; ] <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();
}
}

View file

@ -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;
}
}

View file

@ -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&lt;Person&gt; {
* 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());
}
}

View file

@ -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(&quot;An object: &quot; + 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) &amp;&amp; !f.getName().equals(&quot;password&quot;);
* }
* }).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 &quot;toString()'ed&quot;, 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();
}
}

View file

@ -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

View 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

View file

@ -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
}

View file

@ -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;

View file

@ -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() {}
}

View 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.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);
}
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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;
}

View file

@ -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 &quot;save&quot; 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;
}

View file

@ -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();
}
}
}
}
}

View file

@ -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(&quot;workerthread-%d&quot;)
* .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;
}
}
}

View file

@ -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&lt;Integer&gt; computationCallable = new MyComputationCallable();
* // setup the background initializer
* CallableBackgroundInitializer&lt;Integer&gt; 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");
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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));
}
}

View file

@ -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;
}
}
}

View file

@ -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()));
}
}

View file

@ -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();
}
}
}

View file

@ -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&lt;ComplexObject&gt; {
* &#064;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;
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}

View file

@ -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 &lt;= {@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;
}
}

View file

@ -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., &amp; 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&lt;PrintStream&gt; lock;
*
* public SimpleLogger(OutputStream out) {
* lock = LockingVisitors.stampedLockVisitor(new PrintStream(out));
* }
*
* public void log(String message) {
* lock.acceptWriteLocked((ps) -&gt; ps.println(message));
* }
*
* public void log(byte[] buffer) {
* lock.acceptWriteLocked((ps) -&gt; { 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&lt;List&lt;Object&gt;&gt; listLock;
*
* public int getCurrentListSize() {
* final Integer sizeInteger = listLock.applyReadLocked((list) -&gt; 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