Added /after with WIP on fixed and report
This commit is contained in:
parent
e320c10a38
commit
cdf2205ff5
491 changed files with 182219 additions and 0 deletions
301
.gitignore
vendored
Normal file
301
.gitignore
vendored
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
## Core latex/pdflatex auxiliary files:
|
||||||
|
*.aux
|
||||||
|
*.lof
|
||||||
|
*.log
|
||||||
|
*.lot
|
||||||
|
*.fls
|
||||||
|
*.out
|
||||||
|
*.toc
|
||||||
|
*.fmt
|
||||||
|
*.fot
|
||||||
|
*.cb
|
||||||
|
*.cb2
|
||||||
|
.*.lb
|
||||||
|
|
||||||
|
## Intermediate documents:
|
||||||
|
*.dvi
|
||||||
|
*.xdv
|
||||||
|
*-converted-to.*
|
||||||
|
# these rules might exclude image files for figures etc.
|
||||||
|
# *.ps
|
||||||
|
# *.eps
|
||||||
|
# *.pdf
|
||||||
|
|
||||||
|
## Generated if empty string is given at "Please type another file name for output:"
|
||||||
|
.pdf
|
||||||
|
|
||||||
|
## Bibliography auxiliary files (bibtex/biblatex/biber):
|
||||||
|
*.bbl
|
||||||
|
*.bcf
|
||||||
|
*.blg
|
||||||
|
*-blx.aux
|
||||||
|
*-blx.bib
|
||||||
|
*.run.xml
|
||||||
|
|
||||||
|
## Build tool auxiliary files:
|
||||||
|
*.fdb_latexmk
|
||||||
|
*.synctex
|
||||||
|
*.synctex(busy)
|
||||||
|
*.synctex.gz
|
||||||
|
*.synctex.gz(busy)
|
||||||
|
*.pdfsync
|
||||||
|
|
||||||
|
## Build tool directories for auxiliary files
|
||||||
|
# latexrun
|
||||||
|
latex.out/
|
||||||
|
|
||||||
|
## Auxiliary and intermediate files from other packages:
|
||||||
|
# algorithms
|
||||||
|
*.alg
|
||||||
|
*.loa
|
||||||
|
|
||||||
|
# achemso
|
||||||
|
acs-*.bib
|
||||||
|
|
||||||
|
# amsthm
|
||||||
|
*.thm
|
||||||
|
|
||||||
|
# beamer
|
||||||
|
*.nav
|
||||||
|
*.pre
|
||||||
|
*.snm
|
||||||
|
*.vrb
|
||||||
|
|
||||||
|
# changes
|
||||||
|
*.soc
|
||||||
|
|
||||||
|
# comment
|
||||||
|
*.cut
|
||||||
|
|
||||||
|
# cprotect
|
||||||
|
*.cpt
|
||||||
|
|
||||||
|
# elsarticle (documentclass of Elsevier journals)
|
||||||
|
*.spl
|
||||||
|
|
||||||
|
# endnotes
|
||||||
|
*.ent
|
||||||
|
|
||||||
|
# fixme
|
||||||
|
*.lox
|
||||||
|
|
||||||
|
# feynmf/feynmp
|
||||||
|
*.mf
|
||||||
|
*.mp
|
||||||
|
*.t[1-9]
|
||||||
|
*.t[1-9][0-9]
|
||||||
|
*.tfm
|
||||||
|
|
||||||
|
#(r)(e)ledmac/(r)(e)ledpar
|
||||||
|
*.end
|
||||||
|
*.?end
|
||||||
|
*.[1-9]
|
||||||
|
*.[1-9][0-9]
|
||||||
|
*.[1-9][0-9][0-9]
|
||||||
|
*.[1-9]R
|
||||||
|
*.[1-9][0-9]R
|
||||||
|
*.[1-9][0-9][0-9]R
|
||||||
|
*.eledsec[1-9]
|
||||||
|
*.eledsec[1-9]R
|
||||||
|
*.eledsec[1-9][0-9]
|
||||||
|
*.eledsec[1-9][0-9]R
|
||||||
|
*.eledsec[1-9][0-9][0-9]
|
||||||
|
*.eledsec[1-9][0-9][0-9]R
|
||||||
|
|
||||||
|
# glossaries
|
||||||
|
*.acn
|
||||||
|
*.acr
|
||||||
|
*.glg
|
||||||
|
*.glo
|
||||||
|
*.gls
|
||||||
|
*.glsdefs
|
||||||
|
*.lzo
|
||||||
|
*.lzs
|
||||||
|
*.slg
|
||||||
|
*.slo
|
||||||
|
*.sls
|
||||||
|
|
||||||
|
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
|
||||||
|
# *.ist
|
||||||
|
|
||||||
|
# gnuplot
|
||||||
|
*.gnuplot
|
||||||
|
*.table
|
||||||
|
|
||||||
|
# gnuplottex
|
||||||
|
*-gnuplottex-*
|
||||||
|
|
||||||
|
# gregoriotex
|
||||||
|
*.gaux
|
||||||
|
*.glog
|
||||||
|
*.gtex
|
||||||
|
|
||||||
|
# htlatex
|
||||||
|
*.4ct
|
||||||
|
*.4tc
|
||||||
|
*.idv
|
||||||
|
*.lg
|
||||||
|
*.trc
|
||||||
|
*.xref
|
||||||
|
|
||||||
|
# hyperref
|
||||||
|
*.brf
|
||||||
|
|
||||||
|
# knitr
|
||||||
|
*-concordance.tex
|
||||||
|
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
|
||||||
|
# *.tikz
|
||||||
|
*-tikzDictionary
|
||||||
|
|
||||||
|
# listings
|
||||||
|
*.lol
|
||||||
|
|
||||||
|
# luatexja-ruby
|
||||||
|
*.ltjruby
|
||||||
|
|
||||||
|
# makeidx
|
||||||
|
*.idx
|
||||||
|
*.ilg
|
||||||
|
*.ind
|
||||||
|
|
||||||
|
# minitoc
|
||||||
|
*.maf
|
||||||
|
*.mlf
|
||||||
|
*.mlt
|
||||||
|
*.mtc[0-9]*
|
||||||
|
*.slf[0-9]*
|
||||||
|
*.slt[0-9]*
|
||||||
|
*.stc[0-9]*
|
||||||
|
|
||||||
|
# minted
|
||||||
|
_minted*
|
||||||
|
*.pyg
|
||||||
|
|
||||||
|
# morewrites
|
||||||
|
*.mw
|
||||||
|
|
||||||
|
# newpax
|
||||||
|
*.newpax
|
||||||
|
|
||||||
|
# nomencl
|
||||||
|
*.nlg
|
||||||
|
*.nlo
|
||||||
|
*.nls
|
||||||
|
|
||||||
|
# pax
|
||||||
|
*.pax
|
||||||
|
|
||||||
|
# pdfpcnotes
|
||||||
|
*.pdfpc
|
||||||
|
|
||||||
|
# sagetex
|
||||||
|
*.sagetex.sage
|
||||||
|
*.sagetex.py
|
||||||
|
*.sagetex.scmd
|
||||||
|
|
||||||
|
# scrwfile
|
||||||
|
*.wrt
|
||||||
|
|
||||||
|
# svg
|
||||||
|
svg-inkscape/
|
||||||
|
|
||||||
|
# sympy
|
||||||
|
*.sout
|
||||||
|
*.sympy
|
||||||
|
sympy-plots-for-*.tex/
|
||||||
|
|
||||||
|
# pdfcomment
|
||||||
|
*.upa
|
||||||
|
*.upb
|
||||||
|
|
||||||
|
# pythontex
|
||||||
|
*.pytxcode
|
||||||
|
pythontex-files-*/
|
||||||
|
|
||||||
|
# tcolorbox
|
||||||
|
*.listing
|
||||||
|
|
||||||
|
# thmtools
|
||||||
|
*.loe
|
||||||
|
|
||||||
|
# TikZ & PGF
|
||||||
|
*.dpth
|
||||||
|
*.md5
|
||||||
|
*.auxlock
|
||||||
|
|
||||||
|
# titletoc
|
||||||
|
*.ptc
|
||||||
|
|
||||||
|
# todonotes
|
||||||
|
*.tdo
|
||||||
|
|
||||||
|
# vhistory
|
||||||
|
*.hst
|
||||||
|
*.ver
|
||||||
|
|
||||||
|
# easy-todo
|
||||||
|
*.lod
|
||||||
|
|
||||||
|
# xcolor
|
||||||
|
*.xcp
|
||||||
|
|
||||||
|
# xmpincl
|
||||||
|
*.xmpi
|
||||||
|
|
||||||
|
# xindy
|
||||||
|
*.xdy
|
||||||
|
|
||||||
|
# xypic precompiled matrices and outlines
|
||||||
|
*.xyc
|
||||||
|
*.xyd
|
||||||
|
|
||||||
|
# endfloat
|
||||||
|
*.ttt
|
||||||
|
*.fff
|
||||||
|
|
||||||
|
# Latexian
|
||||||
|
TSWLatexianTemp*
|
||||||
|
|
||||||
|
## Editors:
|
||||||
|
# WinEdt
|
||||||
|
*.bak
|
||||||
|
*.sav
|
||||||
|
|
||||||
|
# Texpad
|
||||||
|
.texpadtmp
|
||||||
|
|
||||||
|
# LyX
|
||||||
|
*.lyx~
|
||||||
|
|
||||||
|
# Kile
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
# gummi
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# KBibTeX
|
||||||
|
*~[0-9]*
|
||||||
|
|
||||||
|
# TeXnicCenter
|
||||||
|
*.tps
|
||||||
|
|
||||||
|
# auto folder when using emacs and auctex
|
||||||
|
./auto/*
|
||||||
|
*.el
|
||||||
|
|
||||||
|
# expex forward references with \gathertags
|
||||||
|
*-tags.tex
|
||||||
|
|
||||||
|
# standalone packages
|
||||||
|
*.sta
|
||||||
|
|
||||||
|
# Makeindex log files
|
||||||
|
*.lpz
|
||||||
|
|
||||||
|
# xwatermark package
|
||||||
|
*.xwm
|
||||||
|
|
||||||
|
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
|
||||||
|
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
|
||||||
|
# Uncomment the next line to have this generated file ignored.
|
||||||
|
#*Notes.bib
|
24
after/.gitattributes
vendored
Normal file
24
after/.gitattributes
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
*.java text diff=java
|
||||||
|
*.html text diff=html
|
||||||
|
*.css text
|
||||||
|
*.js text
|
||||||
|
*.sql text
|
25
after/.github/dependabot.yml
vendored
Normal file
25
after/.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "maven"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
48
after/.github/workflows/maven.yml
vendored
Normal file
48
after/.github/workflows/maven.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
name: Java CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: ${{ matrix.experimental }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java: [ 8, 11, 15 ]
|
||||||
|
experimental: [false]
|
||||||
|
include:
|
||||||
|
- java: 16-ea
|
||||||
|
experimental: true
|
||||||
|
- java: 17-ea
|
||||||
|
experimental: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.3.4
|
||||||
|
- uses: actions/cache@v2.1.4
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
- name: Set up JDK ${{ matrix.java }}
|
||||||
|
uses: actions/setup-java@v1.4.3
|
||||||
|
with:
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
- name: Build with Maven
|
||||||
|
run: mvn -V -Ddoclint=all --file pom.xml --no-transfer-progress
|
28
after/.gitignore
vendored
Normal file
28
after/.gitignore
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Maven repository cache for docker container
|
||||||
|
.m2/
|
||||||
|
|
||||||
|
# infer tmp folder
|
||||||
|
infer-out/tmp/
|
||||||
|
infer-out/bugs.txt
|
||||||
|
infer-out/logs
|
36
after/.travis.yml
Normal file
36
after/.travis.yml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
language: java
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.m2
|
||||||
|
|
||||||
|
jdk:
|
||||||
|
- openjdk8
|
||||||
|
- openjdk11
|
||||||
|
- openjdk15
|
||||||
|
- openjdk-ea
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- jdk: openjdk-ea
|
||||||
|
|
||||||
|
script:
|
||||||
|
- mvn -V --no-transfer-progress
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- mvn -V --no-transfer-progress clean test jacoco:report coveralls:report -Ptravis-jacoco javadoc:javadoc -Ddoclint=all
|
115
after/CONTRIBUTING.md
Normal file
115
after/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<!---
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<!---
|
||||||
|
+======================================================================+
|
||||||
|
|**** ****|
|
||||||
|
|**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
|
||||||
|
|**** DO NOT EDIT DIRECTLY ****|
|
||||||
|
|**** ****|
|
||||||
|
+======================================================================+
|
||||||
|
| TEMPLATE FILE: contributing-md-template.md |
|
||||||
|
| commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
|
||||||
|
+======================================================================+
|
||||||
|
| |
|
||||||
|
| 1) Re-generate using: mvn commons-build:contributing-md |
|
||||||
|
| |
|
||||||
|
| 2) Set the following properties in the component's pom: |
|
||||||
|
| - commons.jira.id (required, alphabetic, upper case) |
|
||||||
|
| |
|
||||||
|
| 3) Example Properties |
|
||||||
|
| |
|
||||||
|
| <properties> |
|
||||||
|
| <commons.jira.id>MATH</commons.jira.id> |
|
||||||
|
| </properties> |
|
||||||
|
| |
|
||||||
|
+======================================================================+
|
||||||
|
--->
|
||||||
|
Contributing to Apache Commons Lang
|
||||||
|
======================
|
||||||
|
|
||||||
|
You have found a bug or you have an idea for a cool new feature? Contributing code is a great way to give something back to
|
||||||
|
the open source community. Before you dig right into the code there are a few guidelines that we need contributors to
|
||||||
|
follow so that we can have a chance of keeping on top of things.
|
||||||
|
|
||||||
|
Getting Started
|
||||||
|
---------------
|
||||||
|
|
||||||
|
+ Make sure you have a [JIRA account](https://issues.apache.org/jira/).
|
||||||
|
+ Make sure you have a [GitHub account](https://github.com/signup/free).
|
||||||
|
+ If you're planning to implement a new feature it makes sense to discuss your changes on the [dev list](https://commons.apache.org/mail-lists.html) first. This way you can make sure you're not wasting your time on something that isn't considered to be in Apache Commons Lang's scope.
|
||||||
|
+ Submit a [Jira Ticket][jira] for your issue, assuming one does not already exist.
|
||||||
|
+ Clearly describe the issue including steps to reproduce when it is a bug.
|
||||||
|
+ Make sure you fill in the earliest version that you know has the issue.
|
||||||
|
+ Find the corresponding [repository on GitHub](https://github.com/apache/?query=commons-),
|
||||||
|
[fork](https://help.github.com/articles/fork-a-repo/) and check out your forked repository.
|
||||||
|
|
||||||
|
Making Changes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
+ Create a _topic branch_ for your isolated work.
|
||||||
|
* Usually you should base your branch on the `master` or `trunk` branch.
|
||||||
|
* A good topic branch name can be the JIRA bug id plus a keyword, e.g. `LANG-123-InputStream`.
|
||||||
|
* If you have submitted multiple JIRA issues, try to maintain separate branches and pull requests.
|
||||||
|
+ Make commits of logical units.
|
||||||
|
* Make sure your commit messages are meaningful and in the proper format. Your commit message should contain the key of the JIRA issue.
|
||||||
|
* e.g. `LANG-123: Close input stream earlier`
|
||||||
|
+ Respect the original code style:
|
||||||
|
+ Only use spaces for indentation.
|
||||||
|
+ Create minimal diffs - disable _On Save_ actions like _Reformat Source Code_ or _Organize Imports_. If you feel the source code should be reformatted create a separate PR for this change first.
|
||||||
|
+ Check for unnecessary whitespace with `git diff` -- check before committing.
|
||||||
|
+ Make sure you have added the necessary tests for your changes, typically in `src/test/java`.
|
||||||
|
+ Run all the tests with `mvn clean verify` to assure nothing else was accidentally broken.
|
||||||
|
|
||||||
|
Making Trivial Changes
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The JIRA tickets are used to generate the changelog for the next release.
|
||||||
|
|
||||||
|
For changes of a trivial nature to comments and documentation, it is not always necessary to create a new ticket in JIRA.
|
||||||
|
In this case, it is appropriate to start the first line of a commit with '(doc)' instead of a ticket number.
|
||||||
|
|
||||||
|
|
||||||
|
Submitting Changes
|
||||||
|
------------------
|
||||||
|
|
||||||
|
+ Sign and submit the Apache [Contributor License Agreement][cla] if you haven't already.
|
||||||
|
* Note that small patches & typical bug fixes do not require a CLA as
|
||||||
|
clause 5 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0.html#contributions)
|
||||||
|
covers them.
|
||||||
|
+ Push your changes to a topic branch in your fork of the repository.
|
||||||
|
+ Submit a _Pull Request_ to the corresponding repository in the `apache` organization.
|
||||||
|
* Verify _Files Changed_ shows only your intended changes and does not
|
||||||
|
include additional files like `target/*.class`
|
||||||
|
+ Update your JIRA ticket and include a link to the pull request in the ticket.
|
||||||
|
|
||||||
|
If you prefer to not use GitHub, then you can instead use
|
||||||
|
`git format-patch` (or `svn diff`) and attach the patch file to the JIRA issue.
|
||||||
|
|
||||||
|
|
||||||
|
Additional Resources
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
+ [Contributing patches](https://commons.apache.org/patches.html)
|
||||||
|
+ [Apache Commons Lang JIRA project page][jira]
|
||||||
|
+ [Contributor License Agreement][cla]
|
||||||
|
+ [General GitHub documentation](https://help.github.com/)
|
||||||
|
+ [GitHub pull request documentation](https://help.github.com/articles/creating-a-pull-request/)
|
||||||
|
+ [Apache Commons Twitter Account](https://twitter.com/ApacheCommons)
|
||||||
|
+ `#apache-commons` IRC channel on `irc.freenode.net`
|
||||||
|
|
||||||
|
[cla]:https://www.apache.org/licenses/#clas
|
||||||
|
[jira]:https://issues.apache.org/jira/browse/LANG
|
115
after/Jenkinsfile
vendored
Normal file
115
after/Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#!groovy
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
node {
|
||||||
|
label 'ubuntu'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tools {
|
||||||
|
maven 'Maven 3 (latest)'
|
||||||
|
jdk 'JDK 1.8 (latest)'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
sh 'mvn'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy') {
|
||||||
|
when {
|
||||||
|
branch 'master'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'mvn deploy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send out notifications on unsuccessful builds.
|
||||||
|
post {
|
||||||
|
// If this build failed, send an email to the list.
|
||||||
|
failure {
|
||||||
|
script {
|
||||||
|
if(env.BRANCH_NAME == "master") {
|
||||||
|
def state = (currentBuild.previousBuild != null) && (currentBuild.previousBuild.result == 'FAILURE') ? "Still failing" : "Failure"
|
||||||
|
emailext(
|
||||||
|
subject: "[Lang] Change on branch \"${env.BRANCH_NAME}\": ${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - $state",
|
||||||
|
body: """The Apache Jenkins build system has built ${env.JOB_NAME} (build #${env.BUILD_NUMBER})
|
||||||
|
|
||||||
|
Status: ${currentBuild.result}
|
||||||
|
|
||||||
|
Check console output at <a href="${env.BUILD_URL}">${env.BUILD_URL}</a> to view the results.
|
||||||
|
""",
|
||||||
|
to: "notifications@commons.apache.org",
|
||||||
|
recipientProviders: [[$class: 'DevelopersRecipientProvider']]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this build didn't fail, but there were failing tests, send an email to the list.
|
||||||
|
unstable {
|
||||||
|
script {
|
||||||
|
if(env.BRANCH_NAME == "master") {
|
||||||
|
def state = (currentBuild.previousBuild != null) && (currentBuild.previousBuild.result == 'UNSTABLE') ? "Still unstable" : "Unstable"
|
||||||
|
emailext(
|
||||||
|
subject: "[Lang] Change on branch \"${env.BRANCH_NAME}\": ${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - $state",
|
||||||
|
body: """The Apache Jenkins build system has built ${env.JOB_NAME} (build #${env.BUILD_NUMBER})
|
||||||
|
|
||||||
|
Status: ${currentBuild.result}
|
||||||
|
|
||||||
|
Check console output at <a href="${env.BUILD_URL}">${env.BUILD_URL}</a> to view the results.
|
||||||
|
""",
|
||||||
|
to: "notifications@commons.apache.org",
|
||||||
|
recipientProviders: [[$class: 'DevelopersRecipientProvider']]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send an email, if the last build was not successful and this one is.
|
||||||
|
success {
|
||||||
|
script {
|
||||||
|
if ((env.BRANCH_NAME == "master") && (currentBuild.previousBuild != null) && (currentBuild.previousBuild.result != 'SUCCESS')) {
|
||||||
|
emailext (
|
||||||
|
subject: "[Lang] Change on branch \"${env.BRANCH_NAME}\": ${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - Back to normal",
|
||||||
|
body: """The Apache Jenkins build system has built ${env.JOB_NAME} (build #${env.BUILD_NUMBER})
|
||||||
|
|
||||||
|
Status: ${currentBuild.result}
|
||||||
|
|
||||||
|
Check console output at <a href="${env.BUILD_URL}">${env.BUILD_URL}</a> to view the results.
|
||||||
|
""",
|
||||||
|
to: "notifications@commons.apache.org",
|
||||||
|
recipientProviders: [[$class: 'DevelopersRecipientProvider']]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
202
after/LICENSE.txt
Normal file
202
after/LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
5
after/NOTICE.txt
Normal file
5
after/NOTICE.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Apache Commons Lang
|
||||||
|
Copyright 2001-2021 The Apache Software Foundation
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
The Apache Software Foundation (https://www.apache.org/).
|
108
after/README.md
Normal file
108
after/README.md
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<!---
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<!---
|
||||||
|
+======================================================================+
|
||||||
|
|**** ****|
|
||||||
|
|**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
|
||||||
|
|**** DO NOT EDIT DIRECTLY ****|
|
||||||
|
|**** ****|
|
||||||
|
+======================================================================+
|
||||||
|
| TEMPLATE FILE: readme-md-template.md |
|
||||||
|
| commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
|
||||||
|
+======================================================================+
|
||||||
|
| |
|
||||||
|
| 1) Re-generate using: mvn commons-build:readme-md |
|
||||||
|
| |
|
||||||
|
| 2) Set the following properties in the component's pom: |
|
||||||
|
| - commons.componentid (required, alphabetic, lower case) |
|
||||||
|
| - commons.release.version (required) |
|
||||||
|
| |
|
||||||
|
| 3) Example Properties |
|
||||||
|
| |
|
||||||
|
| <properties> |
|
||||||
|
| <commons.componentid>math</commons.componentid> |
|
||||||
|
| <commons.release.version>1.2</commons.release.version> |
|
||||||
|
| </properties> |
|
||||||
|
| |
|
||||||
|
+======================================================================+
|
||||||
|
--->
|
||||||
|
Apache Commons Lang
|
||||||
|
===================
|
||||||
|
|
||||||
|
[![Travis-CI Status](https://travis-ci.org/apache/commons-lang.svg)](https://travis-ci.org/apache/commons-lang)
|
||||||
|
[![GitHub Actions Status](https://github.com/apache/commons-lang/workflows/Java%20CI/badge.svg)](https://github.com/apache/commons-lang/actions)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/apache/commons-lang/badge.svg)](https://coveralls.io/r/apache/commons-lang)
|
||||||
|
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.commons/commons-lang3/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.commons/commons-lang3/)
|
||||||
|
[![Javadocs](https://javadoc.io/badge/org.apache.commons/commons-lang3/3.12.0.svg)](https://javadoc.io/doc/org.apache.commons/commons-lang3/3.12.0)
|
||||||
|
|
||||||
|
Apache Commons Lang, a package of Java utility classes for the
|
||||||
|
classes that are in java.lang's hierarchy, or are considered to be so
|
||||||
|
standard as to justify existence in java.lang.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
More information can be found on the [Apache Commons Lang homepage](https://commons.apache.org/proper/commons-lang).
|
||||||
|
The [Javadoc](https://commons.apache.org/proper/commons-lang/apidocs) can be browsed.
|
||||||
|
Questions related to the usage of Apache Commons Lang should be posted to the [user mailing list][ml].
|
||||||
|
|
||||||
|
Where can I get the latest release?
|
||||||
|
-----------------------------------
|
||||||
|
You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-lang/download_lang.cgi).
|
||||||
|
|
||||||
|
Alternatively you can pull it from the central Maven repositories:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.12.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
We accept Pull Requests via GitHub. The [developer mailing list][ml] is the main channel of communication for contributors.
|
||||||
|
There are some guidelines which will make applying PRs easier for us:
|
||||||
|
+ No tabs! Please use spaces for indentation.
|
||||||
|
+ Respect the code style.
|
||||||
|
+ Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change.
|
||||||
|
+ Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running ```mvn clean test```.
|
||||||
|
|
||||||
|
If you plan to contribute on a regular basis, please consider filing a [contributor license agreement](https://www.apache.org/licenses/#clas).
|
||||||
|
You can learn more about contributing via GitHub in our [contribution guidelines](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
This code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
|
See the `NOTICE.txt` file for required notices and attributions.
|
||||||
|
|
||||||
|
Donations
|
||||||
|
---------
|
||||||
|
You like Apache Commons Lang? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development.
|
||||||
|
|
||||||
|
Additional Resources
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
+ [Apache Commons Homepage](https://commons.apache.org/)
|
||||||
|
+ [Apache Issue Tracker (JIRA)](https://issues.apache.org/jira/browse/LANG)
|
||||||
|
+ [Apache Commons Twitter Account](https://twitter.com/ApacheCommons)
|
||||||
|
+ `#apache-commons` IRC channel on `irc.freenode.org`
|
||||||
|
|
||||||
|
[ml]:https://commons.apache.org/mail-lists.html
|
1610
after/RELEASE-NOTES.txt
Normal file
1610
after/RELEASE-NOTES.txt
Normal file
File diff suppressed because it is too large
Load diff
17
after/SECURITY.md
Normal file
17
after/SECURITY.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!---
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
The Apache Commons security page is [https://commons.apache.org/security.html](https://commons.apache.org/security.html).
|
14
after/docker-infer.sh
Executable file
14
after/docker-infer.sh
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
mkdir -pv "${SCRIPT_DIR}/.m2"
|
||||||
|
|
||||||
|
# Cache .m2 directory in local bind mount
|
||||||
|
# I am on an ICE right now. German Wi-Fi is the wurst
|
||||||
|
docker run --rm -it \
|
||||||
|
-v "${SCRIPT_DIR}:/tools/home" \
|
||||||
|
-v "${SCRIPT_DIR}/.m2:/root/.m2" \
|
||||||
|
bugcounting/satools:y23 \
|
||||||
|
/tools/home/entrypoint.sh
|
||||||
|
|
13
after/docker-start.sh
Executable file
13
after/docker-start.sh
Executable file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
mkdir -pv "${SCRIPT_DIR}/.m2"
|
||||||
|
|
||||||
|
# Cache .m2 directory in local bind mount
|
||||||
|
# I am on an ICE right now. German Wi-Fi is the wurst
|
||||||
|
docker run --rm -it \
|
||||||
|
-v "${SCRIPT_DIR}:/tools/home" \
|
||||||
|
-v "${SCRIPT_DIR}/.m2:/root/.m2" \
|
||||||
|
bugcounting/satools:y23
|
||||||
|
|
6
after/entrypoint.sh
Executable file
6
after/entrypoint.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd /tools/home
|
||||||
|
/tools/infer/bin/infer -- mvn compile -Drat.skip=true
|
BIN
after/infer-out/.global.tenv
Normal file
BIN
after/infer-out/.global.tenv
Normal file
Binary file not shown.
1
after/infer-out/.infer_runstate.json
Normal file
1
after/infer-out/.infer_runstate.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"run_sequence":[{"date":"2023-04-11 09:38:59.468025+02:00","command":"run","infer_version":{"major":1,"minor":1,"patch":0,"commit":"f9b6f2b"}}],"results_dir_format":"db_filename: infer-out/results.db\ndb_schema: \n CREATE TABLE IF NOT EXISTS procedures\n ( proc_uid TEXT PRIMARY KEY NOT NULL\n , proc_name BLOB NOT NULL\n , attr_kind INTEGER NOT NULL\n , source_file TEXT NOT NULL\n , proc_attributes BLOB NOT NULL\n , cfg BLOB\n , callees BLOB NOT NULL\n )\n ;\n\n CREATE TABLE IF NOT EXISTS source_files\n ( source_file TEXT PRIMARY KEY\n , type_environment BLOB NOT NULL\n , integer_type_widths BLOB\n , procedure_names BLOB NOT NULL\n , freshly_captured INT NOT NULL )\n ;\n\n CREATE TABLE IF NOT EXISTS specs\n ( proc_uid TEXT PRIMARY KEY NOT NULL\n , proc_name BLOB NOT NULL\n , analysis_summary BLOB NOT NULL\n , report_summary BLOB NOT NULL\n )\n ;\n\n CREATE TABLE IF NOT EXISTS model_specs\n ( proc_uid TEXT PRIMARY KEY NOT NULL\n , proc_name BLOB NOT NULL\n , analysis_summary BLOB NOT NULL\n , report_summary BLOB NOT NULL\n )\n ","should_merge_capture":false}
|
1
after/infer-out/config-impact-report.json
Normal file
1
after/infer-out/config-impact-report.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[]
|
1
after/infer-out/costs-report.json
Normal file
1
after/infer-out/costs-report.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[]
|
Binary file not shown.
1
after/infer-out/report.json
Normal file
1
after/infer-out/report.json
Normal file
File diff suppressed because one or more lines are too long
122
after/infer-out/report.txt
Normal file
122
after/infer-out/report.txt
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#0 DONE
|
||||||
|
src/main/java/org/apache/commons/lang3/AnnotationUtils.java:72: error: Null Dereference
|
||||||
|
object returned by `getAllInterfaces(cls)` could be null and is dereferenced at line 72.
|
||||||
|
70. @Override
|
||||||
|
71. protected String getShortClassName(final Class<?> cls) {
|
||||||
|
72. > for (final Class<?> iface : ClassUtils.getAllInterfaces(cls)) {
|
||||||
|
73. if (Annotation.class.isAssignableFrom(iface)) {
|
||||||
|
74. return "@" + iface.getName();
|
||||||
|
|
||||||
|
#1 DONE
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java:126: error: Null Dereference
|
||||||
|
object returned by `getAllInterfaces(cls)` could be null and is dereferenced at line 126.
|
||||||
|
124. // superclass field.
|
||||||
|
125. Field match = null;
|
||||||
|
126. > for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
|
||||||
|
127. try {
|
||||||
|
128. final Field test = class1.getField(fieldName);
|
||||||
|
|
||||||
|
#2
|
||||||
|
src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java:131: error: Null Dereference
|
||||||
|
object `null` is dereferenced by call to `toString(...)` at line 131.
|
||||||
|
129. */
|
||||||
|
130. public static String toString(final Object object) {
|
||||||
|
131. > return toString(object, null, false, false, null);
|
||||||
|
132. }
|
||||||
|
133.
|
||||||
|
|
||||||
|
#3 false positive, LONG_TO_INT_RANGE.fit never returns null (may throw NPE when param is null, never the case though as actual param is boxed primitive long)
|
||||||
|
src/main/java/org/apache/commons/lang3/time/DurationUtils.java:142: error: Null Dereference
|
||||||
|
object returned by `org.apache.commons.lang3.time.DurationUtils.LONG_TO_INT_RANGE.fit(valueOf(duration.toMillis()))` could be null and is dereferenced at line 142.
|
||||||
|
140. Objects.requireNonNull(duration, "duration");
|
||||||
|
141. // intValue() does not do a narrowing conversion here
|
||||||
|
142. > return LONG_TO_INT_RANGE.fit(Long.valueOf(duration.toMillis())).intValue();
|
||||||
|
143. }
|
||||||
|
144.
|
||||||
|
|
||||||
|
#4 false positive, java.lang.String.toCharArray() always returns a non-null char[] object
|
||||||
|
src/main/java/org/apache/commons/lang3/CharSetUtils.java:181: error: Null Dereference
|
||||||
|
object `chars` last assigned on line 177 could be null and is dereferenced at line 181.
|
||||||
|
179. final char[] chrs = str.toCharArray();
|
||||||
|
180. for (final char chr : chrs) {
|
||||||
|
181. > if (chars.contains(chr) == expect) {
|
||||||
|
182. buffer.append(chr);
|
||||||
|
183. }
|
||||||
|
|
||||||
|
#5
|
||||||
|
src/main/java/org/apache/commons/lang3/builder/ToStringBuilder.java:223: error: Null Dereference
|
||||||
|
object `null` is dereferenced by call to `ToStringBuilder(...)` at line 223.
|
||||||
|
221. */
|
||||||
|
222. public ToStringBuilder(final Object object) {
|
||||||
|
223. > this(object, null, null);
|
||||||
|
224. }
|
||||||
|
225.
|
||||||
|
|
||||||
|
#6 false positive, Validate.notNull(field, ...) throws NPE if field is null thus if the method is call field is non-null
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java:341: error: Null Dereference
|
||||||
|
object `field` last assigned on line 338 could be null and is dereferenced by call to `readStaticField(...)` at line 341.
|
||||||
|
339. Validate.notNull(field, "Cannot locate field '%s' on %s", fieldName, cls);
|
||||||
|
340. // already forced access above, don't repeat it here:
|
||||||
|
341. > return readStaticField(field, false);
|
||||||
|
342. }
|
||||||
|
343.
|
||||||
|
|
||||||
|
#7 false positive, Validate.notNull(field, ...) throws NPE if field is null thus if the method is call field is non-null
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java:385: error: Null Dereference
|
||||||
|
object `field` last assigned on line 382 could be null and is dereferenced by call to `readStaticField(...)` at line 385.
|
||||||
|
383. Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
|
||||||
|
384. // already forced access above, don't repeat it here:
|
||||||
|
385. > return readStaticField(field, false);
|
||||||
|
386. }
|
||||||
|
387.
|
||||||
|
|
||||||
|
#8 DONE
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java:486: error: Null Dereference
|
||||||
|
object returned by `primitiveToWrapper(varArgComponentType)` could be null and is dereferenced by call to `newInstance(...)` at line 486.
|
||||||
|
484. final int varArgLength = args.length - methodParameterTypes.length + 1;
|
||||||
|
485.
|
||||||
|
486. > Object varArgsArray = Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength);
|
||||||
|
487. // Copy the variadic arguments into the varargs array.
|
||||||
|
488. System.arraycopy(args, methodParameterTypes.length - 1, varArgsArray, 0, varArgLength);
|
||||||
|
|
||||||
|
#9 false positive, Validate.notNull(field, ...) throws NPE if field is null thus if the method is call field is non-null
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java:599: error: Null Dereference
|
||||||
|
object `field` last assigned on line 596 could be null and is dereferenced by call to `writeStaticField(...)` at line 599.
|
||||||
|
597. Validate.notNull(field, "Cannot locate field %s on %s", fieldName, cls);
|
||||||
|
598. // already forced access above, don't repeat it here:
|
||||||
|
599. > writeStaticField(field, value, false);
|
||||||
|
600. }
|
||||||
|
601.
|
||||||
|
|
||||||
|
#10 false positive, Validate.notNull(field, ...) throws NPE if field is null thus if the method is call field is non-null
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java:644: error: Null Dereference
|
||||||
|
object `field` last assigned on line 641 could be null and is dereferenced by call to `writeField(...)` at line 644.
|
||||||
|
642. Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
|
||||||
|
643. // already forced access above, don't repeat it here:
|
||||||
|
644. > writeField(field, (Object) null, value, false);
|
||||||
|
645. }
|
||||||
|
646.
|
||||||
|
|
||||||
|
#11 false positive, getAllSuperclassesAndInterfaces(mcls) is non-null if mcls is non-null, mcls is non-null as java.lang.reflect.Method.getDeclaringClass() is always non-null
|
||||||
|
src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java:987: error: Null Dereference
|
||||||
|
object `classes` last assigned on line 986 could be null and is dereferenced at line 987.
|
||||||
|
985. final Class<?> mcls = method.getDeclaringClass();
|
||||||
|
986. final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
|
||||||
|
987. > for (final Class<?> acls : classes) {
|
||||||
|
988. final Method equivalentMethod = (ignoreAccess ? MethodUtils.getMatchingMethod(acls, method.getName(), method.getParameterTypes())
|
||||||
|
989. : MethodUtils.getMatchingAccessibleMethod(acls, method.getName(), method.getParameterTypes()));
|
||||||
|
|
||||||
|
#12
|
||||||
|
src/main/java/org/apache/commons/lang3/concurrent/MultiBackgroundInitializer.java:160: warning: Thread Safety Violation
|
||||||
|
Read/Write race. Non-private method `MultiBackgroundInitializer.getTaskCount()` reads without synchronization from container `this.childInitializers` via call to `Map.values()`. Potentially races with write in method `MultiBackgroundInitializer.addInitializer(...)`.
|
||||||
|
Reporting because another access to the same memory occurs on a background thread, although this access may not.
|
||||||
|
158. int result = 1;
|
||||||
|
159.
|
||||||
|
160. > for (final BackgroundInitializer<?> bi : childInitializers.values()) {
|
||||||
|
161. result += bi.getTaskCount();
|
||||||
|
162. }
|
||||||
|
|
||||||
|
Found 13 issues
|
||||||
|
Issue Type(ISSUED_TYPE_ID): #
|
||||||
|
Null Dereference(NULL_DEREFERENCE): 12
|
||||||
|
Thread Safety Violation(THREAD_SAFETY_VIOLATION): 1
|
BIN
after/infer-out/results.db
Normal file
BIN
after/infer-out/results.db
Normal file
Binary file not shown.
BIN
after/infer-out/results.db-shm
Normal file
BIN
after/infer-out/results.db-shm
Normal file
Binary file not shown.
BIN
after/infer-out/results.db-wal
Normal file
BIN
after/infer-out/results.db-wal
Normal file
Binary file not shown.
1008
after/pom.xml
Normal file
1008
after/pom.xml
Normal file
File diff suppressed because it is too large
Load diff
164
after/spotbugs-exclude-filter.xml
Normal file
164
after/spotbugs-exclude-filter.xml
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file contains some false positive bugs detected by findbugs. Their
|
||||||
|
false positive nature has been analyzed individually and they have been
|
||||||
|
put here to instruct findbugs it must ignore them.
|
||||||
|
-->
|
||||||
|
<FindBugsFilter>
|
||||||
|
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.ArrayUtils" />
|
||||||
|
<Method name="addFirst" />
|
||||||
|
<Bug pattern="NP_LOAD_OF_KNOWN_NULL_VALUE" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: Optimization to use == -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.BooleanUtils" />
|
||||||
|
<Or>
|
||||||
|
<Method name="toBoolean" />
|
||||||
|
<Method name="toBooleanObject" />
|
||||||
|
</Or>
|
||||||
|
<Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ" />
|
||||||
|
</Match>
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.BooleanUtils" />
|
||||||
|
<Method name="toBoolean" />
|
||||||
|
<Bug pattern="RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: Behavior documented in javadoc -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.BooleanUtils" />
|
||||||
|
<Or>
|
||||||
|
<Method name="negate" />
|
||||||
|
<Method name="toBooleanObject" />
|
||||||
|
</Or>
|
||||||
|
<Bug pattern="NP_BOOLEAN_RETURN_NULL" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: base class cannot be changed and field is properly checked against null so behavior is OK -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.text.ExtendedMessageFormat" />
|
||||||
|
<Method name="applyPattern" />
|
||||||
|
<Bug pattern="UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: Optimization to use == -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.StringUtils" />
|
||||||
|
<Or>
|
||||||
|
<Method name="indexOfDifference"/>
|
||||||
|
<Method name="compare" params="java.lang.String,java.lang.String,boolean"/>
|
||||||
|
<Method name="compareIgnoreCase" params="java.lang.String,java.lang.String,boolean"/>
|
||||||
|
</Or>
|
||||||
|
<Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: Very much intended to do a fall through on the switch -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.math.NumberUtils" />
|
||||||
|
<Method name="createNumber"/>
|
||||||
|
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: Very much intended to do a fall through on the switch -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.time.DateUtils" />
|
||||||
|
<Method name="getFragment"/>
|
||||||
|
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: toProperString is lazily loaded -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.math.Fraction" />
|
||||||
|
<Field name="toProperString" />
|
||||||
|
<Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: It does call super.clone(), but via a subsequent method -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.text.StrTokenizer" />
|
||||||
|
<Method name="clone"/>
|
||||||
|
<Bug pattern="CN_IDIOM_NO_SUPER_CALL" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: FindBugs 2.0.2 used in maven-findbugs-plugin 2.5.2 seems to have problems with detection of default cases
|
||||||
|
in switch statements. All the excluded methods have switch statements that conatin a default case. -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.math.NumberUtils"/>
|
||||||
|
<Method name="createNumber" />
|
||||||
|
<Bug pattern="SF_SWITCH_NO_DEFAULT" />
|
||||||
|
</Match>
|
||||||
|
<!-- Reason: FindBugs does not correctly recognize default branches in switch statements without break statements.
|
||||||
|
See, e.g., the report at https://sourceforge.net/p/findbugs/bugs/1298 -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.time.FastDateParser"/>
|
||||||
|
<Or>
|
||||||
|
<Method name="getStrategy" />
|
||||||
|
<Method name="simpleQuote" params="java.lang.StringBuilder, java.lang.String"/>
|
||||||
|
</Or>
|
||||||
|
<Bug pattern="SF_SWITCH_NO_DEFAULT" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: FindBugs cannot correctly recognize default branches in switch statements without break statements.
|
||||||
|
See, e.g., the report at https://sourceforge.net/p/findbugs/bugs/1298 -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.time.FastDatePrinter"/>
|
||||||
|
<Method name="appendFullDigits" params="java.lang.Appendable, int, int"/>
|
||||||
|
<Bug pattern="SF_SWITCH_NO_DEFAULT" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: The fallthrough on the swich statement is intentional -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.time.FastDatePrinter"/>
|
||||||
|
<Method name="appendFullDigits" params="java.lang.Appendable, int, int"/>
|
||||||
|
<Bug pattern="SF_SWITCH_FALLTHROUGH" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: Internal class that is used only as a key for an internal FormatCache. For this reason we can
|
||||||
|
be sure, that equals will never be called with null or types other than MultipartKey.
|
||||||
|
-->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.time.FormatCache$MultipartKey" />
|
||||||
|
<Method name="equals" />
|
||||||
|
<Bug pattern="BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS" />
|
||||||
|
</Match>
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.time.FormatCache$MultipartKey" />
|
||||||
|
<Method name="equals" />
|
||||||
|
<Bug pattern="NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: toString() can return null! -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.compare.ObjectToStringComparator" />
|
||||||
|
<Method name="compare" />
|
||||||
|
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" />
|
||||||
|
</Match>
|
||||||
|
|
||||||
|
<!-- Reason: requireNonNull is supposed to take a nullable parameter,
|
||||||
|
whatever Spotbugs thinks of it. -->
|
||||||
|
<Match>
|
||||||
|
<Class name="org.apache.commons.lang3.function.Objects" />
|
||||||
|
<Method name="requireNonNull" />
|
||||||
|
<Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" />
|
||||||
|
</Match>
|
||||||
|
</FindBugsFilter>
|
46
after/src/assembly/bin.xml
Normal file
46
after/src/assembly/bin.xml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<assembly>
|
||||||
|
<id>bin</id>
|
||||||
|
<formats>
|
||||||
|
<format>tar.gz</format>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<includeSiteDirectory>false</includeSiteDirectory>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<includes>
|
||||||
|
<include>LICENSE.txt</include>
|
||||||
|
<include>NOTICE.txt</include>
|
||||||
|
<include>RELEASE-NOTES.txt</include>
|
||||||
|
<include>README.md</include>
|
||||||
|
<include>CONTRIBUTING.md</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target</directory>
|
||||||
|
<outputDirectory></outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/site/apidocs</directory>
|
||||||
|
<outputDirectory>apidocs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
44
after/src/assembly/src.xml
Normal file
44
after/src/assembly/src.xml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<assembly>
|
||||||
|
<id>src</id>
|
||||||
|
<formats>
|
||||||
|
<format>tar.gz</format>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<baseDirectory>${project.artifactId}-${commons.release.version}-src</baseDirectory>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<includes>
|
||||||
|
<include>.travis.yml</include>
|
||||||
|
<include>checkstyle.xml</include>
|
||||||
|
<include>checkstyle-suppressions.xml</include>
|
||||||
|
<include>spotbugs-exclude-filter.xml</include>
|
||||||
|
<include>LICENSE.txt</include>
|
||||||
|
<include>NOTICE.txt</include>
|
||||||
|
<include>pom.xml</include>
|
||||||
|
<include>PROPOSAL.html</include>
|
||||||
|
<include>RELEASE-NOTES.txt</include>
|
||||||
|
<include>README.md</include>
|
||||||
|
<include>CONTRIBUTING.md</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>src</directory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
1223
after/src/changes/changes.xml
Normal file
1223
after/src/changes/changes.xml
Normal file
File diff suppressed because it is too large
Load diff
144
after/src/changes/release-notes.vm
Normal file
144
after/src/changes/release-notes.vm
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
## Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
## or more contributor license agreements. See the NOTICE file
|
||||||
|
## distributed with this work for additional information
|
||||||
|
## regarding copyright ownership. The ASF licenses this file
|
||||||
|
## to you under the Apache License, Version 2.0 (the
|
||||||
|
## "License"); you may not use this file except in compliance
|
||||||
|
## with the License. You may obtain a copy of the License at
|
||||||
|
##
|
||||||
|
## http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
##
|
||||||
|
## Unless required by applicable law or agreed to in writing,
|
||||||
|
## software distributed under the License is distributed on an
|
||||||
|
## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
## KIND, either express or implied. See the License for the
|
||||||
|
## specific language governing permissions and limitations
|
||||||
|
## under the License.
|
||||||
|
##
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
${project.name}
|
||||||
|
Version ${version}
|
||||||
|
Release Notes
|
||||||
|
|
||||||
|
|
||||||
|
INTRODUCTION:
|
||||||
|
|
||||||
|
This document contains the release notes for the ${version} version of Apache Commons Lang.
|
||||||
|
Commons Lang is a set of utility functions and reusable components that should be of use in any
|
||||||
|
Java environment.
|
||||||
|
|
||||||
|
Lang 3.9 and onwards now targets Java 8, making use of features that arrived with Java 8.
|
||||||
|
|
||||||
|
For the advice on upgrading from 2.x to 3.x, see the following page:
|
||||||
|
|
||||||
|
https://commons.apache.org/lang/article3_0.html
|
||||||
|
|
||||||
|
$introduction.replaceAll("(?<!\015)\012", "
|
||||||
|
").replaceAll("(?m)^ +","")
|
||||||
|
|
||||||
|
## N.B. the available variables are described here:
|
||||||
|
## http://maven.apache.org/plugins/maven-changes-plugin/examples/using-a-custom-announcement-template.html
|
||||||
|
##
|
||||||
|
## Hack to improve layout: replace all pairs of spaces with a single new-line
|
||||||
|
$release.description.replaceAll(" ", "
|
||||||
|
")
|
||||||
|
|
||||||
|
## set up indent sizes. Only change indent1
|
||||||
|
#set($props=${project.properties})
|
||||||
|
#set($jiralen=$props.get("commons.jira.id").length())
|
||||||
|
## indent1 = POOL-nnnn:
|
||||||
|
#set($blanklen=$jiralen+6)## +6 for "-nnnn:"
|
||||||
|
## must be at least as long as the longest JIRA id
|
||||||
|
#set($blanks=" ")
|
||||||
|
#set($indent1=$blanks.substring(0,$blanklen))
|
||||||
|
## indent2 allows for issue wrapper
|
||||||
|
#set($indent2="$indent1 ")
|
||||||
|
##
|
||||||
|
#macro ( processaction )
|
||||||
|
## Use replaceAll to fix up LF-only line ends on Windows.
|
||||||
|
#set($action=$actionItem.getAction().replaceAll("\n","
|
||||||
|
"))
|
||||||
|
## Fix up indentation for multi-line action descriptions
|
||||||
|
#set($action=$action.replaceAll("(?m)^ +",$indent2))
|
||||||
|
#if ($actionItem.getIssue())
|
||||||
|
#set($issue="$actionItem.getIssue():")
|
||||||
|
## Pad shorter issue numbers
|
||||||
|
#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
|
||||||
|
#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
|
||||||
|
#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
|
||||||
|
#else
|
||||||
|
#set($issue=$indent1)
|
||||||
|
#end
|
||||||
|
#if ($actionItem.getDueTo())
|
||||||
|
#set($dueto=" Thanks to $actionItem.getDueTo().")
|
||||||
|
#else
|
||||||
|
#set($dueto="")
|
||||||
|
#end
|
||||||
|
o $issue ${action}$dueto
|
||||||
|
#set($action="")
|
||||||
|
#set($issue="")
|
||||||
|
#set($dueto="")
|
||||||
|
#end
|
||||||
|
##
|
||||||
|
#if ($release.getActions().size() == 0)
|
||||||
|
No changes defined in this version.
|
||||||
|
#else
|
||||||
|
Changes in this version include:
|
||||||
|
|
||||||
|
#if ($release.getActions('add').size() !=0)
|
||||||
|
New features:
|
||||||
|
#foreach($actionItem in $release.getActions('add'))
|
||||||
|
#processaction()
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
|
||||||
|
#if ($release.getActions('fix').size() !=0)
|
||||||
|
Fixed Bugs:
|
||||||
|
#foreach($actionItem in $release.getActions('fix'))
|
||||||
|
#processaction()
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
|
||||||
|
#if ($release.getActions('update').size() !=0)
|
||||||
|
Changes:
|
||||||
|
#foreach($actionItem in $release.getActions('update'))
|
||||||
|
#processaction()
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
|
||||||
|
#if ($release.getActions('remove').size() !=0)
|
||||||
|
Removed:
|
||||||
|
#foreach($actionItem in $release.getActions('remove'))
|
||||||
|
#processaction()
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
## End of main loop
|
||||||
|
#end
|
||||||
|
|
||||||
|
Historical list of changes: ${project.url}changes-report.html
|
||||||
|
|
||||||
|
For complete information on ${project.name}, including instructions on how to submit bug reports,
|
||||||
|
patches, or suggestions for improvement, see the Apache ${project.name} website:
|
||||||
|
|
||||||
|
${project.url}
|
||||||
|
|
||||||
|
Download page: ${project.url}download_lang.cgi
|
||||||
|
|
||||||
|
Have fun!
|
||||||
|
-Apache Commons Team
|
|
@ -0,0 +1,368 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.List;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
final List<Class<?>> interfaces = ClassUtils.getAllInterfaces(cls);
|
||||||
|
if (interfaces == null) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Class<?> iface : interfaces) {
|
||||||
|
if (Annotation.class.isAssignableFrom(iface)) {
|
||||||
|
return "@" + iface.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) {
|
||||||
|
if (value instanceof Annotation) {
|
||||||
|
value = AnnotationUtils.toString((Annotation) value);
|
||||||
|
}
|
||||||
|
super.appendDetail(buffer, fieldName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code AnnotationUtils} instances should NOT be constructed in
|
||||||
|
* standard programming. Instead, the class should be used statically.</p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean
|
||||||
|
* instance to operate.</p>
|
||||||
|
*/
|
||||||
|
public AnnotationUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Checks if two annotations are equal using the criteria for equality
|
||||||
|
* presented in the {@link Annotation#equals(Object)} API docs.</p>
|
||||||
|
*
|
||||||
|
* @param a1 the first Annotation to compare, {@code null} returns
|
||||||
|
* {@code false} unless both are {@code null}
|
||||||
|
* @param a2 the second Annotation to compare, {@code null} returns
|
||||||
|
* {@code false} unless both are {@code null}
|
||||||
|
* @return {@code true} if the two annotations are {@code equal} or both
|
||||||
|
* {@code null}
|
||||||
|
*/
|
||||||
|
public static boolean equals(final Annotation a1, final Annotation a2) {
|
||||||
|
if (a1 == a2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a1 == null || a2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Class<? extends Annotation> type1 = a1.annotationType();
|
||||||
|
final Class<? extends Annotation> type2 = a2.annotationType();
|
||||||
|
Validate.notNull(type1, "Annotation %s with null annotationType()", a1);
|
||||||
|
Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
|
||||||
|
if (!type1.equals(type2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (final Method m : type1.getDeclaredMethods()) {
|
||||||
|
if (m.getParameterTypes().length == 0
|
||||||
|
&& isValidAnnotationMemberType(m.getReturnType())) {
|
||||||
|
final Object v1 = m.invoke(a1);
|
||||||
|
final Object v2 = m.invoke(a2);
|
||||||
|
if (!memberEquals(m.getReturnType(), v1, v2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IllegalAccessException | InvocationTargetException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Generate a hash code for the given annotation using the algorithm
|
||||||
|
* presented in the {@link Annotation#hashCode()} API docs.</p>
|
||||||
|
*
|
||||||
|
* @param a the Annotation for a hash code calculation is desired, not
|
||||||
|
* {@code null}
|
||||||
|
* @return the calculated hash code
|
||||||
|
* @throws RuntimeException if an {@code Exception} is encountered during
|
||||||
|
* annotation member access
|
||||||
|
* @throws IllegalStateException if an annotation method invocation returns
|
||||||
|
* {@code null}
|
||||||
|
*/
|
||||||
|
public static int hashCode(final Annotation a) {
|
||||||
|
int result = 0;
|
||||||
|
final Class<? extends Annotation> type = a.annotationType();
|
||||||
|
for (final Method m : type.getDeclaredMethods()) {
|
||||||
|
try {
|
||||||
|
final Object value = m.invoke(a);
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format("Annotation method %s returned null", m));
|
||||||
|
}
|
||||||
|
result += hashMember(m.getName(), value);
|
||||||
|
} catch (final RuntimeException ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Generate a string representation of an Annotation, as suggested by
|
||||||
|
* {@link Annotation#toString()}.</p>
|
||||||
|
*
|
||||||
|
* @param a the annotation of which a string representation is desired
|
||||||
|
* @return the standard string representation of an annotation, not
|
||||||
|
* {@code null}
|
||||||
|
*/
|
||||||
|
public static String toString(final Annotation a) {
|
||||||
|
final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE);
|
||||||
|
for (final Method m : a.annotationType().getDeclaredMethods()) {
|
||||||
|
if (m.getParameterTypes().length > 0) {
|
||||||
|
continue; //wtf?
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
builder.append(m.getName(), m.invoke(a));
|
||||||
|
} catch (final RuntimeException ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if the specified type is permitted as an annotation member.</p>
|
||||||
|
*
|
||||||
|
* <p>The Java language specification only permits certain types to be used
|
||||||
|
* in annotations. These include {@link String}, {@link Class}, primitive
|
||||||
|
* types, {@link Annotation}, {@link Enum}, and single-dimensional arrays of
|
||||||
|
* these types.</p>
|
||||||
|
*
|
||||||
|
* @param type the type to check, {@code null}
|
||||||
|
* @return {@code true} if the type is a valid type to use in an annotation
|
||||||
|
*/
|
||||||
|
public static boolean isValidAnnotationMemberType(Class<?> type) {
|
||||||
|
if (type == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type.isArray()) {
|
||||||
|
type = type.getComponentType();
|
||||||
|
}
|
||||||
|
return type.isPrimitive() || type.isEnum() || type.isAnnotation()
|
||||||
|
|| String.class.equals(type) || Class.class.equals(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
//besides modularity, this has the advantage of autoboxing primitives:
|
||||||
|
/**
|
||||||
|
* Helper method for generating a hash code for a member of an annotation.
|
||||||
|
*
|
||||||
|
* @param name the name of the member
|
||||||
|
* @param value the value of the member
|
||||||
|
* @return a hash code for this member
|
||||||
|
*/
|
||||||
|
private static int hashMember(final String name, final Object value) {
|
||||||
|
final int part1 = name.hashCode() * 127;
|
||||||
|
if (value.getClass().isArray()) {
|
||||||
|
return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
|
||||||
|
}
|
||||||
|
if (value instanceof Annotation) {
|
||||||
|
return part1 ^ hashCode((Annotation) value);
|
||||||
|
}
|
||||||
|
return part1 ^ value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for checking whether two objects of the given type are
|
||||||
|
* equal. This method is used to compare the parameters of two annotation
|
||||||
|
* instances.
|
||||||
|
*
|
||||||
|
* @param type the type of the objects to be compared
|
||||||
|
* @param o1 the first object
|
||||||
|
* @param o2 the second object
|
||||||
|
* @return a flag whether these objects are equal
|
||||||
|
*/
|
||||||
|
private static boolean memberEquals(final Class<?> type, final Object o1, final Object o2) {
|
||||||
|
if (o1 == o2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o1 == null || o2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type.isArray()) {
|
||||||
|
return arrayMemberEquals(type.getComponentType(), o1, o2);
|
||||||
|
}
|
||||||
|
if (type.isAnnotation()) {
|
||||||
|
return equals((Annotation) o1, (Annotation) o2);
|
||||||
|
}
|
||||||
|
return o1.equals(o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for comparing two objects of an array type.
|
||||||
|
*
|
||||||
|
* @param componentType the component type of the array
|
||||||
|
* @param o1 the first object
|
||||||
|
* @param o2 the second object
|
||||||
|
* @return a flag whether these objects are equal
|
||||||
|
*/
|
||||||
|
private static boolean arrayMemberEquals(final Class<?> componentType, final Object o1, final Object o2) {
|
||||||
|
if (componentType.isAnnotation()) {
|
||||||
|
return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Byte.TYPE)) {
|
||||||
|
return Arrays.equals((byte[]) o1, (byte[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Short.TYPE)) {
|
||||||
|
return Arrays.equals((short[]) o1, (short[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Integer.TYPE)) {
|
||||||
|
return Arrays.equals((int[]) o1, (int[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Character.TYPE)) {
|
||||||
|
return Arrays.equals((char[]) o1, (char[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Long.TYPE)) {
|
||||||
|
return Arrays.equals((long[]) o1, (long[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Float.TYPE)) {
|
||||||
|
return Arrays.equals((float[]) o1, (float[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Double.TYPE)) {
|
||||||
|
return Arrays.equals((double[]) o1, (double[]) o2);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Boolean.TYPE)) {
|
||||||
|
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
|
||||||
|
}
|
||||||
|
return Arrays.equals((Object[]) o1, (Object[]) o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for comparing two arrays of annotations.
|
||||||
|
*
|
||||||
|
* @param a1 the first array
|
||||||
|
* @param a2 the second array
|
||||||
|
* @return a flag whether these arrays are equal
|
||||||
|
*/
|
||||||
|
private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) {
|
||||||
|
if (a1.length != a2.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < a1.length; i++) {
|
||||||
|
if (!equals(a1[i], a2[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for generating a hash code for an array.
|
||||||
|
*
|
||||||
|
* @param componentType the component type of the array
|
||||||
|
* @param o the array
|
||||||
|
* @return a hash code for the specified array
|
||||||
|
*/
|
||||||
|
private static int arrayMemberHash(final Class<?> componentType, final Object o) {
|
||||||
|
if (componentType.equals(Byte.TYPE)) {
|
||||||
|
return Arrays.hashCode((byte[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Short.TYPE)) {
|
||||||
|
return Arrays.hashCode((short[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Integer.TYPE)) {
|
||||||
|
return Arrays.hashCode((int[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Character.TYPE)) {
|
||||||
|
return Arrays.hashCode((char[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Long.TYPE)) {
|
||||||
|
return Arrays.hashCode((long[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Float.TYPE)) {
|
||||||
|
return Arrays.hashCode((float[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Double.TYPE)) {
|
||||||
|
return Arrays.hashCode((double[]) o);
|
||||||
|
}
|
||||||
|
if (componentType.equals(Boolean.TYPE)) {
|
||||||
|
return Arrays.hashCode((boolean[]) o);
|
||||||
|
}
|
||||||
|
return Arrays.hashCode((Object[]) o);
|
||||||
|
}
|
||||||
|
}
|
132
after/src/main/java/org/apache/commons/lang3/ArchUtils.java
Normal file
132
after/src/main/java/org/apache/commons/lang3/ArchUtils.java
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.arch.Processor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An utility class for the os.arch System Property. The class defines methods for
|
||||||
|
* identifying the architecture of the current JVM.
|
||||||
|
* <p>
|
||||||
|
* Important: The os.arch System Property returns the architecture used by the JVM
|
||||||
|
* not of the operating system.
|
||||||
|
* </p>
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public class ArchUtils {
|
||||||
|
|
||||||
|
private static final Map<String, Processor> ARCH_TO_PROCESSOR;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ARCH_TO_PROCESSOR = new HashMap<>();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init() {
|
||||||
|
init_X86_32Bit();
|
||||||
|
init_X86_64Bit();
|
||||||
|
init_IA64_32Bit();
|
||||||
|
init_IA64_64Bit();
|
||||||
|
init_PPC_32Bit();
|
||||||
|
init_PPC_64Bit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init_X86_32Bit() {
|
||||||
|
final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.X86);
|
||||||
|
addProcessors(processor, "x86", "i386", "i486", "i586", "i686", "pentium");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init_X86_64Bit() {
|
||||||
|
final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.X86);
|
||||||
|
addProcessors(processor, "x86_64", "amd64", "em64t", "universal");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init_IA64_32Bit() {
|
||||||
|
final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.IA_64);
|
||||||
|
addProcessors(processor, "ia64_32", "ia64n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init_IA64_64Bit() {
|
||||||
|
final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.IA_64);
|
||||||
|
addProcessors(processor, "ia64", "ia64w");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init_PPC_32Bit() {
|
||||||
|
final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.PPC);
|
||||||
|
addProcessors(processor, "ppc", "power", "powerpc", "power_pc", "power_rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init_PPC_64Bit() {
|
||||||
|
final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.PPC);
|
||||||
|
addProcessors(processor, "ppc64", "power64", "powerpc64", "power_pc64", "power_rs64");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given {@link Processor} with the given key {@link String} to the map.
|
||||||
|
*
|
||||||
|
* @param key The key as {@link String}.
|
||||||
|
* @param processor The {@link Processor} to add.
|
||||||
|
* @throws IllegalStateException If the key already exists.
|
||||||
|
*/
|
||||||
|
private static void addProcessor(final String key, final Processor processor) {
|
||||||
|
if (ARCH_TO_PROCESSOR.containsKey(key)) {
|
||||||
|
throw new IllegalStateException("Key " + key + " already exists in processor map");
|
||||||
|
}
|
||||||
|
ARCH_TO_PROCESSOR.put(key, processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given {@link Processor} with the given keys to the map.
|
||||||
|
*
|
||||||
|
* @param keys The keys.
|
||||||
|
* @param processor The {@link Processor} to add.
|
||||||
|
* @throws IllegalStateException If the key already exists.
|
||||||
|
*/
|
||||||
|
private static void addProcessors(final Processor processor, final String... keys) {
|
||||||
|
Stream.of(keys).forEach(e -> addProcessor(e, processor));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Processor} object of the current JVM.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Important: The os.arch System Property returns the architecture used by the JVM
|
||||||
|
* not of the operating system.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return A {@link Processor} when supported, else {@code null}.
|
||||||
|
*/
|
||||||
|
public static Processor getProcessor() {
|
||||||
|
return getProcessor(SystemUtils.OS_ARCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Processor} object the given value {@link String}. The {@link String} must be
|
||||||
|
* like a value returned by the os.arch System Property.
|
||||||
|
*
|
||||||
|
* @param value A {@link String} like a value returned by the os.arch System Property.
|
||||||
|
* @return A {@link Processor} when it exists, else {@code null}.
|
||||||
|
*/
|
||||||
|
public static Processor getProcessor(final String value) {
|
||||||
|
return ARCH_TO_PROCESSOR.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
141
after/src/main/java/org/apache/commons/lang3/ArraySorter.java
Normal file
141
after/src/main/java/org/apache/commons/lang3/ArraySorter.java
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns arrays in the fluent style.
|
||||||
|
*
|
||||||
|
* @since 3.12.0
|
||||||
|
*/
|
||||||
|
public class ArraySorter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(byte[])
|
||||||
|
*/
|
||||||
|
public static byte[] sort(final byte[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(char[])
|
||||||
|
*/
|
||||||
|
public static char[] sort(final char[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(double[])
|
||||||
|
*/
|
||||||
|
public static double[] sort(final double[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(float[])
|
||||||
|
*/
|
||||||
|
public static float[] sort(final float[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(int[])
|
||||||
|
*/
|
||||||
|
public static int[] sort(final int[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(long[])
|
||||||
|
*/
|
||||||
|
public static long[] sort(final long[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(short[])
|
||||||
|
*/
|
||||||
|
public static short[] sort(final short[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param <T> the array type.
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(Object[])
|
||||||
|
*/
|
||||||
|
public static <T> T[] sort(final T[] array) {
|
||||||
|
Arrays.sort(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts and returns the given array.
|
||||||
|
*
|
||||||
|
* @param <T> the array type.
|
||||||
|
* @param array the array to sort.
|
||||||
|
* @param comparator the comparator to determine the order of the array. A {@code null} value uses the elements'
|
||||||
|
* {@link Comparable natural ordering}.
|
||||||
|
* @return the given array.
|
||||||
|
* @see Arrays#sort(Object[])
|
||||||
|
*/
|
||||||
|
public static <T> T[] sort(final T[] array, final Comparator<? super T> comparator) {
|
||||||
|
Arrays.sort(array, comparator);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9654
after/src/main/java/org/apache/commons/lang3/ArrayUtils.java
Normal file
9654
after/src/main/java/org/apache/commons/lang3/ArrayUtils.java
Normal file
File diff suppressed because it is too large
Load diff
322
after/src/main/java/org/apache/commons/lang3/BitField.java
Normal file
322
after/src/main/java/org/apache/commons/lang3/BitField.java
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Supports operations on bit-mapped fields. Instances of this class can be
|
||||||
|
* used to store a flag or data within an {@code int}, {@code short} or
|
||||||
|
* {@code byte}.</p>
|
||||||
|
*
|
||||||
|
* <p>Each {@code BitField} is constructed with a mask value, which indicates
|
||||||
|
* the bits that will be used to store and retrieve the data for that field.
|
||||||
|
* For instance, the mask {@code 0xFF} indicates the least-significant byte
|
||||||
|
* should be used to store the data.</p>
|
||||||
|
*
|
||||||
|
* <p>As an example, consider a car painting machine that accepts
|
||||||
|
* paint instructions as integers. Bit fields can be used to encode this:</p>
|
||||||
|
*
|
||||||
|
*<pre>
|
||||||
|
* // blue, green and red are 1 byte values (0-255) stored in the three least
|
||||||
|
* // significant bytes
|
||||||
|
* BitField blue = new BitField(0xFF);
|
||||||
|
* BitField green = new BitField(0xFF00);
|
||||||
|
* BitField red = new BitField(0xFF0000);
|
||||||
|
*
|
||||||
|
* // anyColor is a flag triggered if any color is used
|
||||||
|
* BitField anyColor = new BitField(0xFFFFFF);
|
||||||
|
*
|
||||||
|
* // isMetallic is a single bit flag
|
||||||
|
* BitField isMetallic = new BitField(0x1000000);
|
||||||
|
*</pre>
|
||||||
|
*
|
||||||
|
* <p>Using these {@code BitField} instances, a paint instruction can be
|
||||||
|
* encoded into an integer:</p>
|
||||||
|
*
|
||||||
|
*<pre>
|
||||||
|
* int paintInstruction = 0;
|
||||||
|
* paintInstruction = red.setValue(paintInstruction, 35);
|
||||||
|
* paintInstruction = green.setValue(paintInstruction, 100);
|
||||||
|
* paintInstruction = blue.setValue(paintInstruction, 255);
|
||||||
|
*</pre>
|
||||||
|
*
|
||||||
|
* <p>Flags and data can be retrieved from the integer:</p>
|
||||||
|
*
|
||||||
|
*<pre>
|
||||||
|
* // Prints true if red, green or blue is non-zero
|
||||||
|
* System.out.println(anyColor.isSet(paintInstruction)); // prints true
|
||||||
|
*
|
||||||
|
* // Prints value of red, green and blue
|
||||||
|
* System.out.println(red.getValue(paintInstruction)); // prints 35
|
||||||
|
* System.out.println(green.getValue(paintInstruction)); // prints 100
|
||||||
|
* System.out.println(blue.getValue(paintInstruction)); // prints 255
|
||||||
|
*
|
||||||
|
* // Prints true if isMetallic was set
|
||||||
|
* System.out.println(isMetallic.isSet(paintInstruction)); // prints false
|
||||||
|
*</pre>
|
||||||
|
*
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class BitField {
|
||||||
|
|
||||||
|
private final int _mask;
|
||||||
|
private final int _shift_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a BitField instance.</p>
|
||||||
|
*
|
||||||
|
* @param mask the mask specifying which bits apply to this
|
||||||
|
* BitField. Bits that are set in this mask are the bits
|
||||||
|
* that this BitField operates on
|
||||||
|
*/
|
||||||
|
public BitField(final int mask) {
|
||||||
|
_mask = mask;
|
||||||
|
_shift_count = mask == 0 ? 0 : Integer.numberOfTrailingZeros(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the value for the specified BitField, appropriately
|
||||||
|
* shifted right.</p>
|
||||||
|
*
|
||||||
|
* <p>Many users of a BitField will want to treat the specified
|
||||||
|
* bits as an int value, and will not want to be aware that the
|
||||||
|
* value is stored as a BitField (and so shifted left so many
|
||||||
|
* bits).</p>
|
||||||
|
*
|
||||||
|
* @see #setValue(int,int)
|
||||||
|
* @param holder the int data containing the bits we're interested
|
||||||
|
* in
|
||||||
|
* @return the selected bits, shifted right appropriately
|
||||||
|
*/
|
||||||
|
public int getValue(final int holder) {
|
||||||
|
return getRawValue(holder) >> _shift_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the value for the specified BitField, appropriately
|
||||||
|
* shifted right, as a short.</p>
|
||||||
|
*
|
||||||
|
* <p>Many users of a BitField will want to treat the specified
|
||||||
|
* bits as an int value, and will not want to be aware that the
|
||||||
|
* value is stored as a BitField (and so shifted left so many
|
||||||
|
* bits).</p>
|
||||||
|
*
|
||||||
|
* @see #setShortValue(short,short)
|
||||||
|
* @param holder the short data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the selected bits, shifted right appropriately
|
||||||
|
*/
|
||||||
|
public short getShortValue(final short holder) {
|
||||||
|
return (short) getValue(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the value for the specified BitField, unshifted.</p>
|
||||||
|
*
|
||||||
|
* @param holder the int data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the selected bits
|
||||||
|
*/
|
||||||
|
public int getRawValue(final int holder) {
|
||||||
|
return holder & _mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the value for the specified BitField, unshifted.</p>
|
||||||
|
*
|
||||||
|
* @param holder the short data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the selected bits
|
||||||
|
*/
|
||||||
|
public short getShortRawValue(final short holder) {
|
||||||
|
return (short) getRawValue(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns whether the field is set or not.</p>
|
||||||
|
*
|
||||||
|
* <p>This is most commonly used for a single-bit field, which is
|
||||||
|
* often used to represent a boolean value; the results of using
|
||||||
|
* it for a multi-bit field is to determine whether *any* of its
|
||||||
|
* bits are set.</p>
|
||||||
|
*
|
||||||
|
* @param holder the int data containing the bits we're interested
|
||||||
|
* in
|
||||||
|
* @return {@code true} if any of the bits are set,
|
||||||
|
* else {@code false}
|
||||||
|
*/
|
||||||
|
public boolean isSet(final int holder) {
|
||||||
|
return (holder & _mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns whether all of the bits are set or not.</p>
|
||||||
|
*
|
||||||
|
* <p>This is a stricter test than {@link #isSet(int)},
|
||||||
|
* in that all of the bits in a multi-bit set must be set
|
||||||
|
* for this method to return {@code true}.</p>
|
||||||
|
*
|
||||||
|
* @param holder the int data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return {@code true} if all of the bits are set,
|
||||||
|
* else {@code false}
|
||||||
|
*/
|
||||||
|
public boolean isAllSet(final int holder) {
|
||||||
|
return (holder & _mask) == _mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces the bits with new values.</p>
|
||||||
|
*
|
||||||
|
* @see #getValue(int)
|
||||||
|
* @param holder the int data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @param value the new value for the specified bits
|
||||||
|
* @return the value of holder with the bits from the value
|
||||||
|
* parameter replacing the old bits
|
||||||
|
*/
|
||||||
|
public int setValue(final int holder, final int value) {
|
||||||
|
return (holder & ~_mask) | ((value << _shift_count) & _mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces the bits with new values.</p>
|
||||||
|
*
|
||||||
|
* @see #getShortValue(short)
|
||||||
|
* @param holder the short data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @param value the new value for the specified bits
|
||||||
|
* @return the value of holder with the bits from the value
|
||||||
|
* parameter replacing the old bits
|
||||||
|
*/
|
||||||
|
public short setShortValue(final short holder, final short value) {
|
||||||
|
return (short) setValue(holder, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears the bits.</p>
|
||||||
|
*
|
||||||
|
* @param holder the int data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the value of holder with the specified bits cleared
|
||||||
|
* (set to {@code 0})
|
||||||
|
*/
|
||||||
|
public int clear(final int holder) {
|
||||||
|
return holder & ~_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears the bits.</p>
|
||||||
|
*
|
||||||
|
* @param holder the short data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the value of holder with the specified bits cleared
|
||||||
|
* (set to {@code 0})
|
||||||
|
*/
|
||||||
|
public short clearShort(final short holder) {
|
||||||
|
return (short) clear(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears the bits.</p>
|
||||||
|
*
|
||||||
|
* @param holder the byte data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
*
|
||||||
|
* @return the value of holder with the specified bits cleared
|
||||||
|
* (set to {@code 0})
|
||||||
|
*/
|
||||||
|
public byte clearByte(final byte holder) {
|
||||||
|
return (byte) clear(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the bits.</p>
|
||||||
|
*
|
||||||
|
* @param holder the int data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the value of holder with the specified bits set
|
||||||
|
* to {@code 1}
|
||||||
|
*/
|
||||||
|
public int set(final int holder) {
|
||||||
|
return holder | _mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the bits.</p>
|
||||||
|
*
|
||||||
|
* @param holder the short data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @return the value of holder with the specified bits set
|
||||||
|
* to {@code 1}
|
||||||
|
*/
|
||||||
|
public short setShort(final short holder) {
|
||||||
|
return (short) set(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the bits.</p>
|
||||||
|
*
|
||||||
|
* @param holder the byte data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
*
|
||||||
|
* @return the value of holder with the specified bits set
|
||||||
|
* to {@code 1}
|
||||||
|
*/
|
||||||
|
public byte setByte(final byte holder) {
|
||||||
|
return (byte) set(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets a boolean BitField.</p>
|
||||||
|
*
|
||||||
|
* @param holder the int data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @param flag indicating whether to set or clear the bits
|
||||||
|
* @return the value of holder with the specified bits set or
|
||||||
|
* cleared
|
||||||
|
*/
|
||||||
|
public int setBoolean(final int holder, final boolean flag) {
|
||||||
|
return flag ? set(holder) : clear(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets a boolean BitField.</p>
|
||||||
|
*
|
||||||
|
* @param holder the short data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @param flag indicating whether to set or clear the bits
|
||||||
|
* @return the value of holder with the specified bits set or
|
||||||
|
* cleared
|
||||||
|
*/
|
||||||
|
public short setShortBoolean(final short holder, final boolean flag) {
|
||||||
|
return flag ? setShort(holder) : clearShort(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets a boolean BitField.</p>
|
||||||
|
*
|
||||||
|
* @param holder the byte data containing the bits we're
|
||||||
|
* interested in
|
||||||
|
* @param flag indicating whether to set or clear the bits
|
||||||
|
* @return the value of holder with the specified bits set or
|
||||||
|
* cleared
|
||||||
|
*/
|
||||||
|
public byte setByteBoolean(final byte holder, final boolean flag) {
|
||||||
|
return flag ? setByte(holder) : clearByte(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1139
after/src/main/java/org/apache/commons/lang3/BooleanUtils.java
Normal file
1139
after/src/main/java/org/apache/commons/lang3/BooleanUtils.java
Normal file
File diff suppressed because it is too large
Load diff
110
after/src/main/java/org/apache/commons/lang3/CharEncoding.java
Normal file
110
after/src/main/java/org/apache/commons/lang3/CharEncoding.java
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.IllegalCharsetNameException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Character encoding names required of every implementation of the Java platform.</p>
|
||||||
|
*
|
||||||
|
* <p>According to <a href="http://docs.oracle.com/javase/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
|
||||||
|
* encoding names</a>:</p>
|
||||||
|
*
|
||||||
|
* <p><cite>Every implementation of the Java platform is required to support the following character encodings.
|
||||||
|
* Consult the release documentation for your implementation to see if any other encodings are supported.
|
||||||
|
* </cite></p>
|
||||||
|
*
|
||||||
|
* @see <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html">JRE character encoding names</a>
|
||||||
|
* @since 2.1
|
||||||
|
* @deprecated Java 7 introduced {@link java.nio.charset.StandardCharsets}, which defines these constants as
|
||||||
|
* {@link Charset} objects. Use {@link Charset#name()} to get the string values provided in this class.
|
||||||
|
* This class will be removed in a future release.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class CharEncoding {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>ISO Latin Alphabet #1, also known as ISO-LATIN-1.</p>
|
||||||
|
*
|
||||||
|
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||||
|
*/
|
||||||
|
public static final String ISO_8859_1 = "ISO-8859-1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block
|
||||||
|
* of the Unicode character set.</p>
|
||||||
|
*
|
||||||
|
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||||
|
*/
|
||||||
|
public static final String US_ASCII = "US-ASCII";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial
|
||||||
|
* byte-order mark (either order accepted on input, big-endian used on output).</p>
|
||||||
|
*
|
||||||
|
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||||
|
*/
|
||||||
|
public static final String UTF_16 = "UTF-16";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sixteen-bit Unicode Transformation Format, big-endian byte order.</p>
|
||||||
|
*
|
||||||
|
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||||
|
*/
|
||||||
|
public static final String UTF_16BE = "UTF-16BE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sixteen-bit Unicode Transformation Format, little-endian byte order.</p>
|
||||||
|
*
|
||||||
|
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||||
|
*/
|
||||||
|
public static final String UTF_16LE = "UTF-16LE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Eight-bit Unicode Transformation Format.</p>
|
||||||
|
*
|
||||||
|
* <p>Every implementation of the Java platform is required to support this character encoding.</p>
|
||||||
|
*/
|
||||||
|
public static final String UTF_8 = "UTF-8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns whether the named charset is supported.</p>
|
||||||
|
*
|
||||||
|
* <p>This is similar to <a
|
||||||
|
* href="http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html#isSupported%28java.lang.String%29">
|
||||||
|
* java.nio.charset.Charset.isSupported(String)</a> but handles more formats</p>
|
||||||
|
*
|
||||||
|
* @param name the name of the requested charset; may be either a canonical name or an alias, null returns false
|
||||||
|
* @return {@code true} if the charset is available in the current Java virtual machine
|
||||||
|
* @deprecated Please use {@link Charset#isSupported(String)} instead, although be aware that {@code null}
|
||||||
|
* values are not accepted by that method and an {@link IllegalCharsetNameException} may be thrown.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isSupported(final String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Charset.isSupported(name);
|
||||||
|
} catch (final IllegalCharsetNameException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
367
after/src/main/java/org/apache/commons/lang3/CharRange.java
Normal file
367
after/src/main/java/org/apache/commons/lang3/CharRange.java
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A contiguous range of characters, optionally negated.</p>
|
||||||
|
*
|
||||||
|
* <p>Instances are immutable.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
// TODO: This is no longer public and will be removed later as CharSet is moved
|
||||||
|
// to depend on Range.
|
||||||
|
final class CharRange implements Iterable<Character>, Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support. Lang version 2.0.
|
||||||
|
*
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8270183163158333422L;
|
||||||
|
|
||||||
|
/** The first character, inclusive, in the range. */
|
||||||
|
private final char start;
|
||||||
|
/** The last character, inclusive, in the range. */
|
||||||
|
private final char end;
|
||||||
|
/** True if the range is everything except the characters specified. */
|
||||||
|
private final boolean negated;
|
||||||
|
|
||||||
|
/** Cached toString. */
|
||||||
|
private transient String iToString;
|
||||||
|
|
||||||
|
/** Empty array. */
|
||||||
|
static final CharRange[] EMPTY_ARRAY = new CharRange[0];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a {@code CharRange} over a set of characters,
|
||||||
|
* optionally negating the range.</p>
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* start and end characters.</p>
|
||||||
|
*
|
||||||
|
* <p>If start and end are in the wrong order, they are reversed.
|
||||||
|
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||||
|
*
|
||||||
|
* @param start first character, inclusive, in this range
|
||||||
|
* @param end last character, inclusive, in this range
|
||||||
|
* @param negated true to express everything except the range
|
||||||
|
*/
|
||||||
|
private CharRange(char start, char end, final boolean negated) {
|
||||||
|
if (start > end) {
|
||||||
|
final char temp = start;
|
||||||
|
start = end;
|
||||||
|
end = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.negated = negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a {@code CharRange} over a single character.</p>
|
||||||
|
*
|
||||||
|
* @param ch only character in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange is(final char ch) {
|
||||||
|
return new CharRange(ch, ch, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a negated {@code CharRange} over a single character.</p>
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* single character.</p>
|
||||||
|
*
|
||||||
|
* @param ch only character in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange isNot(final char ch) {
|
||||||
|
return new CharRange(ch, ch, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a {@code CharRange} over a set of characters.</p>
|
||||||
|
*
|
||||||
|
* <p>If start and end are in the wrong order, they are reversed.
|
||||||
|
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||||
|
*
|
||||||
|
* @param start first character, inclusive, in this range
|
||||||
|
* @param end last character, inclusive, in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange isIn(final char start, final char end) {
|
||||||
|
return new CharRange(start, end, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a negated {@code CharRange} over a set of characters.</p>
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* start and end characters.</p>
|
||||||
|
*
|
||||||
|
* <p>If start and end are in the wrong order, they are reversed.
|
||||||
|
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||||
|
*
|
||||||
|
* @param start first character, inclusive, in this range
|
||||||
|
* @param end last character, inclusive, in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange isNotIn(final char start, final char end) {
|
||||||
|
return new CharRange(start, end, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Gets the start character for this character range.</p>
|
||||||
|
*
|
||||||
|
* @return the start char (inclusive)
|
||||||
|
*/
|
||||||
|
public char getStart() {
|
||||||
|
return this.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the end character for this character range.</p>
|
||||||
|
*
|
||||||
|
* @return the end char (inclusive)
|
||||||
|
*/
|
||||||
|
public char getEnd() {
|
||||||
|
return this.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Is this {@code CharRange} negated.</p>
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* start and end characters.</p>
|
||||||
|
*
|
||||||
|
* @return {@code true} if negated
|
||||||
|
*/
|
||||||
|
public boolean isNegated() {
|
||||||
|
return negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Is the character specified contained in this range.</p>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return {@code true} if this range contains the input character
|
||||||
|
*/
|
||||||
|
public boolean contains(final char ch) {
|
||||||
|
return (ch >= start && ch <= end) != negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Are all the characters of the passed in range contained in
|
||||||
|
* this range.</p>
|
||||||
|
*
|
||||||
|
* @param range the range to check against
|
||||||
|
* @return {@code true} if this range entirely contains the input range
|
||||||
|
* @throws IllegalArgumentException if {@code null} input
|
||||||
|
*/
|
||||||
|
public boolean contains(final CharRange range) {
|
||||||
|
Validate.notNull(range, "range");
|
||||||
|
if (negated) {
|
||||||
|
if (range.negated) {
|
||||||
|
return start >= range.start && end <= range.end;
|
||||||
|
}
|
||||||
|
return range.end < start || range.start > end;
|
||||||
|
}
|
||||||
|
if (range.negated) {
|
||||||
|
return start == 0 && end == Character.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return start <= range.start && end >= range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basics
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Compares two CharRange objects, returning true if they represent
|
||||||
|
* exactly the same range of characters defined in the same way.</p>
|
||||||
|
*
|
||||||
|
* @param obj the object to compare to
|
||||||
|
* @return true if equal
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof CharRange)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final CharRange other = (CharRange) obj;
|
||||||
|
return start == other.start && end == other.end && negated == other.negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets a hashCode compatible with the equals method.</p>
|
||||||
|
*
|
||||||
|
* @return a suitable hashCode
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 83 + start + 7 * end + (negated ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets a string representation of the character range.</p>
|
||||||
|
*
|
||||||
|
* @return string representation of this range
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (iToString == null) {
|
||||||
|
final StringBuilder buf = new StringBuilder(4);
|
||||||
|
if (isNegated()) {
|
||||||
|
buf.append('^');
|
||||||
|
}
|
||||||
|
buf.append(start);
|
||||||
|
if (start != end) {
|
||||||
|
buf.append('-');
|
||||||
|
buf.append(end);
|
||||||
|
}
|
||||||
|
iToString = buf.toString();
|
||||||
|
}
|
||||||
|
return iToString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expansions
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Returns an iterator which can be used to walk through the characters described by this range.</p>
|
||||||
|
*
|
||||||
|
* <p>#NotThreadSafe# the iterator is not thread-safe</p>
|
||||||
|
* @return an iterator to the chars represented by this range
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Character> iterator() {
|
||||||
|
return new CharacterIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character {@link Iterator}.
|
||||||
|
* <p>#NotThreadSafe#</p>
|
||||||
|
*/
|
||||||
|
private static class CharacterIterator implements Iterator<Character> {
|
||||||
|
/** The current character */
|
||||||
|
private char current;
|
||||||
|
|
||||||
|
private final CharRange range;
|
||||||
|
private boolean hasNext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new iterator for the character range.
|
||||||
|
*
|
||||||
|
* @param r The character range
|
||||||
|
*/
|
||||||
|
private CharacterIterator(final CharRange r) {
|
||||||
|
range = r;
|
||||||
|
hasNext = true;
|
||||||
|
|
||||||
|
if (range.negated) {
|
||||||
|
if (range.start == 0) {
|
||||||
|
if (range.end == Character.MAX_VALUE) {
|
||||||
|
// This range is an empty set
|
||||||
|
hasNext = false;
|
||||||
|
} else {
|
||||||
|
current = (char) (range.end + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = range.start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the next character in the range.
|
||||||
|
*/
|
||||||
|
private void prepareNext() {
|
||||||
|
if (range.negated) {
|
||||||
|
if (current == Character.MAX_VALUE) {
|
||||||
|
hasNext = false;
|
||||||
|
} else if (current + 1 == range.start) {
|
||||||
|
if (range.end == Character.MAX_VALUE) {
|
||||||
|
hasNext = false;
|
||||||
|
} else {
|
||||||
|
current = (char) (range.end + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = (char) (current + 1);
|
||||||
|
}
|
||||||
|
} else if (current < range.end) {
|
||||||
|
current = (char) (current + 1);
|
||||||
|
} else {
|
||||||
|
hasNext = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has the iterator not reached the end character yet?
|
||||||
|
*
|
||||||
|
* @return {@code true} if the iterator has yet to reach the character date
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next character in the iteration
|
||||||
|
*
|
||||||
|
* @return {@code Character} for the next character
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Character next() {
|
||||||
|
if (!hasNext) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
final char cur = current;
|
||||||
|
prepareNext();
|
||||||
|
return Character.valueOf(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always throws UnsupportedOperationException.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException Always thrown.
|
||||||
|
* @see java.util.Iterator#remove()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,383 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Operations on {@link CharSequence} that are
|
||||||
|
* {@code null} safe.</p>
|
||||||
|
*
|
||||||
|
* @see CharSequence
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class CharSequenceUtils {
|
||||||
|
|
||||||
|
private static final int NOT_FOUND = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code CharSequenceUtils} instances should NOT be constructed in
|
||||||
|
* standard programming. </p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean
|
||||||
|
* instance to operate.</p>
|
||||||
|
*/
|
||||||
|
public CharSequenceUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Returns a new {@code CharSequence} that is a subsequence of this
|
||||||
|
* sequence starting with the {@code char} value at the specified index.</p>
|
||||||
|
*
|
||||||
|
* <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
|
||||||
|
* The length (in {@code char}) of the returned sequence is {@code length() - start},
|
||||||
|
* so if {@code start == end} then an empty sequence is returned.</p>
|
||||||
|
*
|
||||||
|
* @param cs the specified subsequence, null returns null
|
||||||
|
* @param start the start index, inclusive, valid
|
||||||
|
* @return a new subsequence, may be null
|
||||||
|
* @throws IndexOutOfBoundsException if {@code start} is negative or if
|
||||||
|
* {@code start} is greater than {@code length()}
|
||||||
|
*/
|
||||||
|
public static CharSequence subSequence(final CharSequence cs, final int start) {
|
||||||
|
return cs == null ? null : cs.subSequence(start, cs.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Returns the index within {@code cs} of the first occurrence of the
|
||||||
|
* specified character, starting the search at the specified index.
|
||||||
|
* <p>
|
||||||
|
* If a character with value {@code searchChar} occurs in the
|
||||||
|
* character sequence represented by the {@code cs}
|
||||||
|
* object at an index no smaller than {@code start}, then
|
||||||
|
* the index of the first such occurrence is returned. For values
|
||||||
|
* of {@code searchChar} in the range from 0 to 0xFFFF (inclusive),
|
||||||
|
* this is the smallest value <i>k</i> such that:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* (this.charAt(<i>k</i>) == searchChar) && (<i>k</i> >= start)
|
||||||
|
* </pre></blockquote>
|
||||||
|
* is true. For other values of {@code searchChar}, it is the
|
||||||
|
* smallest value <i>k</i> such that:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* (this.codePointAt(<i>k</i>) == searchChar) && (<i>k</i> >= start)
|
||||||
|
* </pre></blockquote>
|
||||||
|
* is true. In either case, if no such character occurs inm {@code cs}
|
||||||
|
* at or after position {@code start}, then
|
||||||
|
* {@code -1} is returned.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* There is no restriction on the value of {@code start}. If it
|
||||||
|
* is negative, it has the same effect as if it were zero: the entire
|
||||||
|
* {@code CharSequence} may be searched. If it is greater than
|
||||||
|
* the length of {@code cs}, it has the same effect as if it were
|
||||||
|
* equal to the length of {@code cs}: {@code -1} is returned.
|
||||||
|
*
|
||||||
|
* <p>All indices are specified in {@code char} values
|
||||||
|
* (Unicode code units).
|
||||||
|
*
|
||||||
|
* @param cs the {@code CharSequence} to be processed, not null
|
||||||
|
* @param searchChar the char to be searched for
|
||||||
|
* @param start the start index, negative starts at the string start
|
||||||
|
* @return the index where the search char was found, -1 if not found
|
||||||
|
* @since 3.6 updated to behave more like {@code String}
|
||||||
|
*/
|
||||||
|
static int indexOf(final CharSequence cs, final int searchChar, int start) {
|
||||||
|
if (cs instanceof String) {
|
||||||
|
return ((String) cs).indexOf(searchChar, start);
|
||||||
|
}
|
||||||
|
final int sz = cs.length();
|
||||||
|
if (start < 0) {
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||||
|
for (int i = start; i < sz; i++) {
|
||||||
|
if (cs.charAt(i) == searchChar) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
//supplementary characters (LANG1300)
|
||||||
|
if (searchChar <= Character.MAX_CODE_POINT) {
|
||||||
|
final char[] chars = Character.toChars(searchChar);
|
||||||
|
for (int i = start; i < sz - 1; i++) {
|
||||||
|
final char high = cs.charAt(i);
|
||||||
|
final char low = cs.charAt(i + 1);
|
||||||
|
if (high == chars[0] && low == chars[1]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
|
||||||
|
*
|
||||||
|
* @param cs the {@code CharSequence} to be processed
|
||||||
|
* @param searchChar the {@code CharSequence} to be searched for
|
||||||
|
* @param start the start index
|
||||||
|
* @return the index where the search sequence was found
|
||||||
|
*/
|
||||||
|
static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
|
||||||
|
if (cs instanceof String) {
|
||||||
|
return ((String) cs).indexOf(searchChar.toString(), start);
|
||||||
|
} else if (cs instanceof StringBuilder) {
|
||||||
|
return ((StringBuilder) cs).indexOf(searchChar.toString(), start);
|
||||||
|
} else if (cs instanceof StringBuffer) {
|
||||||
|
return ((StringBuffer) cs).indexOf(searchChar.toString(), start);
|
||||||
|
}
|
||||||
|
return cs.toString().indexOf(searchChar.toString(), start);
|
||||||
|
// if (cs instanceof String && searchChar instanceof String) {
|
||||||
|
// // TODO: Do we assume searchChar is usually relatively small;
|
||||||
|
// // If so then calling toString() on it is better than reverting to
|
||||||
|
// // the green implementation in the else block
|
||||||
|
// return ((String) cs).indexOf((String) searchChar, start);
|
||||||
|
// } else {
|
||||||
|
// // TODO: Implement rather than convert to String
|
||||||
|
// return cs.toString().indexOf(searchChar.toString(), start);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index within {@code cs} of the last occurrence of
|
||||||
|
* the specified character, searching backward starting at the
|
||||||
|
* specified index. For values of {@code searchChar} in the range
|
||||||
|
* from 0 to 0xFFFF (inclusive), the index returned is the largest
|
||||||
|
* value <i>k</i> such that:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* (this.charAt(<i>k</i>) == searchChar) && (<i>k</i> <= start)
|
||||||
|
* </pre></blockquote>
|
||||||
|
* is true. For other values of {@code searchChar}, it is the
|
||||||
|
* largest value <i>k</i> such that:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* (this.codePointAt(<i>k</i>) == searchChar) && (<i>k</i> <= start)
|
||||||
|
* </pre></blockquote>
|
||||||
|
* is true. In either case, if no such character occurs in {@code cs}
|
||||||
|
* at or before position {@code start}, then {@code -1} is returned.
|
||||||
|
*
|
||||||
|
* <p>All indices are specified in {@code char} values
|
||||||
|
* (Unicode code units).
|
||||||
|
*
|
||||||
|
* @param cs the {@code CharSequence} to be processed
|
||||||
|
* @param searchChar the char to be searched for
|
||||||
|
* @param start the start index, negative returns -1, beyond length starts at end
|
||||||
|
* @return the index where the search char was found, -1 if not found
|
||||||
|
* @since 3.6 updated to behave more like {@code String}
|
||||||
|
*/
|
||||||
|
static int lastIndexOf(final CharSequence cs, final int searchChar, int start) {
|
||||||
|
if (cs instanceof String) {
|
||||||
|
return ((String) cs).lastIndexOf(searchChar, start);
|
||||||
|
}
|
||||||
|
final int sz = cs.length();
|
||||||
|
if (start < 0) {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
if (start >= sz) {
|
||||||
|
start = sz - 1;
|
||||||
|
}
|
||||||
|
if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||||
|
for (int i = start; i >= 0; --i) {
|
||||||
|
if (cs.charAt(i) == searchChar) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
//supplementary characters (LANG1300)
|
||||||
|
//NOTE - we must do a forward traversal for this to avoid duplicating code points
|
||||||
|
if (searchChar <= Character.MAX_CODE_POINT) {
|
||||||
|
final char[] chars = Character.toChars(searchChar);
|
||||||
|
//make sure it's not the last index
|
||||||
|
if (start == sz - 1) {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
for (int i = start; i >= 0; i--) {
|
||||||
|
final char high = cs.charAt(i);
|
||||||
|
final char low = cs.charAt(i + 1);
|
||||||
|
if (chars[0] == high && chars[1] == low) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int TO_STRING_LIMIT = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
|
||||||
|
*
|
||||||
|
* @param cs the {@code CharSequence} to be processed
|
||||||
|
* @param searchChar the {@code CharSequence} to find
|
||||||
|
* @param start the start index
|
||||||
|
* @return the index where the search sequence was found
|
||||||
|
*/
|
||||||
|
static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, int start) {
|
||||||
|
if (searchChar == null || cs == null) {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
if (searchChar instanceof String) {
|
||||||
|
if (cs instanceof String) {
|
||||||
|
return ((String) cs).lastIndexOf((String) searchChar, start);
|
||||||
|
} else if (cs instanceof StringBuilder) {
|
||||||
|
return ((StringBuilder) cs).lastIndexOf((String) searchChar, start);
|
||||||
|
} else if (cs instanceof StringBuffer) {
|
||||||
|
return ((StringBuffer) cs).lastIndexOf((String) searchChar, start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int len1 = cs.length();
|
||||||
|
final int len2 = searchChar.length();
|
||||||
|
|
||||||
|
if (start > len1) {
|
||||||
|
start = len1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < 0 || len2 < 0 || len2 > len1) {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len2 == 0) {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len2 <= TO_STRING_LIMIT) {
|
||||||
|
if (cs instanceof String) {
|
||||||
|
return ((String) cs).lastIndexOf(searchChar.toString(), start);
|
||||||
|
} else if (cs instanceof StringBuilder) {
|
||||||
|
return ((StringBuilder) cs).lastIndexOf(searchChar.toString(), start);
|
||||||
|
} else if (cs instanceof StringBuffer) {
|
||||||
|
return ((StringBuffer) cs).lastIndexOf(searchChar.toString(), start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start + len2 > len1) {
|
||||||
|
start = len1 - len2;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char char0 = searchChar.charAt(0);
|
||||||
|
|
||||||
|
int i = start;
|
||||||
|
while (true) {
|
||||||
|
while (cs.charAt(i) != char0) {
|
||||||
|
i--;
|
||||||
|
if (i < 0) {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (checkLaterThan1(cs, searchChar, len2, i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
if (i < 0) {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkLaterThan1(final CharSequence cs, final CharSequence searchChar, final int len2, final int start1) {
|
||||||
|
for (int i = 1, j = len2 - 1; i <= j; i++, j--) {
|
||||||
|
if (cs.charAt(start1 + i) != searchChar.charAt(i)
|
||||||
|
||
|
||||||
|
cs.charAt(start1 + j) != searchChar.charAt(j)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given CharSequence to a char[].
|
||||||
|
*
|
||||||
|
* @param source the {@code CharSequence} to be processed.
|
||||||
|
* @return the resulting char array, never null.
|
||||||
|
* @since 3.11
|
||||||
|
*/
|
||||||
|
public static char[] toCharArray(final CharSequence source) {
|
||||||
|
final int len = StringUtils.length(source);
|
||||||
|
if (len == 0) {
|
||||||
|
return ArrayUtils.EMPTY_CHAR_ARRAY;
|
||||||
|
}
|
||||||
|
if (source instanceof String) {
|
||||||
|
return ((String) source).toCharArray();
|
||||||
|
}
|
||||||
|
final char[] array = new char[len];
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
array[i] = source.charAt(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Green implementation of regionMatches.
|
||||||
|
*
|
||||||
|
* @param cs the {@code CharSequence} to be processed
|
||||||
|
* @param ignoreCase whether or not to be case insensitive
|
||||||
|
* @param thisStart the index to start on the {@code cs} CharSequence
|
||||||
|
* @param substring the {@code CharSequence} to be looked for
|
||||||
|
* @param start the index to start on the {@code substring} CharSequence
|
||||||
|
* @param length character length of the region
|
||||||
|
* @return whether the region matched
|
||||||
|
*/
|
||||||
|
static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart,
|
||||||
|
final CharSequence substring, final int start, final int length) {
|
||||||
|
if (cs instanceof String && substring instanceof String) {
|
||||||
|
return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
|
||||||
|
}
|
||||||
|
int index1 = thisStart;
|
||||||
|
int index2 = start;
|
||||||
|
int tmpLen = length;
|
||||||
|
|
||||||
|
// Extract these first so we detect NPEs the same as the java.lang.String version
|
||||||
|
final int srcLen = cs.length() - thisStart;
|
||||||
|
final int otherLen = substring.length() - start;
|
||||||
|
|
||||||
|
// Check for invalid parameters
|
||||||
|
if (thisStart < 0 || start < 0 || length < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the regions are long enough
|
||||||
|
if (srcLen < length || otherLen < length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (tmpLen-- > 0) {
|
||||||
|
final char c1 = cs.charAt(index1++);
|
||||||
|
final char c2 = substring.charAt(index2++);
|
||||||
|
|
||||||
|
if (c1 == c2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreCase) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The real same check as in String.regionMatches():
|
||||||
|
final char u1 = Character.toUpperCase(c1);
|
||||||
|
final char u2 = Character.toUpperCase(c2);
|
||||||
|
if (u1 != u2 && Character.toLowerCase(u1) != Character.toLowerCase(u2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
295
after/src/main/java/org/apache/commons/lang3/CharSet.java
Normal file
295
after/src/main/java/org/apache/commons/lang3/CharSet.java
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A set of characters.</p>
|
||||||
|
*
|
||||||
|
* <p>Instances are immutable, but instances of subclasses may not be.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class CharSet implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support. Lang version 2.0.
|
||||||
|
*
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5947847346149275958L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CharSet defining no characters.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static final CharSet EMPTY = new CharSet((String) null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CharSet defining ASCII alphabetic characters "a-zA-Z".
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static final CharSet ASCII_ALPHA = new CharSet("a-zA-Z");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CharSet defining ASCII alphabetic characters "a-z".
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static final CharSet ASCII_ALPHA_LOWER = new CharSet("a-z");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CharSet defining ASCII alphabetic characters "A-Z".
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static final CharSet ASCII_ALPHA_UPPER = new CharSet("A-Z");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CharSet defining ASCII alphabetic characters "0-9".
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static final CharSet ASCII_NUMERIC = new CharSet("0-9");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Map of the common cases used in the factory.
|
||||||
|
* Subclasses can add more common patterns if desired
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
protected static final Map<String, CharSet> COMMON = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
static {
|
||||||
|
COMMON.put(null, EMPTY);
|
||||||
|
COMMON.put(StringUtils.EMPTY, EMPTY);
|
||||||
|
COMMON.put("a-zA-Z", ASCII_ALPHA);
|
||||||
|
COMMON.put("A-Za-z", ASCII_ALPHA);
|
||||||
|
COMMON.put("a-z", ASCII_ALPHA_LOWER);
|
||||||
|
COMMON.put("A-Z", ASCII_ALPHA_UPPER);
|
||||||
|
COMMON.put("0-9", ASCII_NUMERIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The set of CharRange objects. */
|
||||||
|
private final Set<CharRange> set = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Factory method to create a new CharSet using a special syntax.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code null} or empty string ("")
|
||||||
|
* - set containing no characters</li>
|
||||||
|
* <li>Single character, such as "a"
|
||||||
|
* - set containing just that character</li>
|
||||||
|
* <li>Multi character, such as "a-e"
|
||||||
|
* - set containing characters from one character to the other</li>
|
||||||
|
* <li>Negated, such as "^a" or "^a-e"
|
||||||
|
* - set containing all characters except those defined</li>
|
||||||
|
* <li>Combinations, such as "abe-g"
|
||||||
|
* - set containing all the characters from the individual sets</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>The matching order is:</p>
|
||||||
|
* <ol>
|
||||||
|
* <li>Negated multi character range, such as "^a-e"
|
||||||
|
* <li>Ordinary multi character range, such as "a-e"
|
||||||
|
* <li>Negated single character, such as "^a"
|
||||||
|
* <li>Ordinary single character, such as "a"
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>Matching works left to right. Once a match is found the
|
||||||
|
* search starts again from the next character.</p>
|
||||||
|
*
|
||||||
|
* <p>If the same range is defined twice using the same syntax, only
|
||||||
|
* one range will be kept.
|
||||||
|
* Thus, "a-ca-c" creates only one range of "a-c".</p>
|
||||||
|
*
|
||||||
|
* <p>If the start and end of a range are in the wrong order,
|
||||||
|
* they are reversed. Thus "a-e" is the same as "e-a".
|
||||||
|
* As a result, "a-ee-a" would create only one range,
|
||||||
|
* as the "a-e" and "e-a" are the same.</p>
|
||||||
|
*
|
||||||
|
* <p>The set of characters represented is the union of the specified ranges.</p>
|
||||||
|
*
|
||||||
|
* <p>There are two ways to add a literal negation character ({@code ^}):</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>As the last character in a string, e.g. {@code CharSet.getInstance("a-z^")}</li>
|
||||||
|
* <li>As a separate element, e.g. {@code CharSet.getInstance("^", "a-z")}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Examples using the negation character:</p>
|
||||||
|
* <pre>
|
||||||
|
* CharSet.getInstance("^a-c").contains('a') = false
|
||||||
|
* CharSet.getInstance("^a-c").contains('d') = true
|
||||||
|
* CharSet.getInstance("^^a-c").contains('a') = true // (only '^' is negated)
|
||||||
|
* CharSet.getInstance("^^a-c").contains('^') = false
|
||||||
|
* CharSet.getInstance("^a-cd-f").contains('d') = true
|
||||||
|
* CharSet.getInstance("a-c^").contains('^') = true
|
||||||
|
* CharSet.getInstance("^", "a-c").contains('^') = true
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>All CharSet objects returned by this method will be immutable.</p>
|
||||||
|
*
|
||||||
|
* @param setStrs Strings to merge into the set, may be null
|
||||||
|
* @return a CharSet instance
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
public static CharSet getInstance(final String... setStrs) {
|
||||||
|
if (setStrs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (setStrs.length == 1) {
|
||||||
|
final CharSet common = COMMON.get(setStrs[0]);
|
||||||
|
if (common != null) {
|
||||||
|
return common;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new CharSet(setStrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Constructs a new CharSet using the set syntax.
|
||||||
|
* Each string is merged in with the set.</p>
|
||||||
|
*
|
||||||
|
* @param set Strings to merge into the initial set
|
||||||
|
* @throws NullPointerException if set is {@code null}
|
||||||
|
*/
|
||||||
|
protected CharSet(final String... set) {
|
||||||
|
for (final String s : set) {
|
||||||
|
add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Add a set definition string to the {@code CharSet}.</p>
|
||||||
|
*
|
||||||
|
* @param str set definition string
|
||||||
|
*/
|
||||||
|
protected void add(final String str) {
|
||||||
|
if (str == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int len = str.length();
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < len) {
|
||||||
|
final int remainder = len - pos;
|
||||||
|
if (remainder >= 4 && str.charAt(pos) == '^' && str.charAt(pos + 2) == '-') {
|
||||||
|
// negated range
|
||||||
|
set.add(CharRange.isNotIn(str.charAt(pos + 1), str.charAt(pos + 3)));
|
||||||
|
pos += 4;
|
||||||
|
} else if (remainder >= 3 && str.charAt(pos + 1) == '-') {
|
||||||
|
// range
|
||||||
|
set.add(CharRange.isIn(str.charAt(pos), str.charAt(pos + 2)));
|
||||||
|
pos += 3;
|
||||||
|
} else if (remainder >= 2 && str.charAt(pos) == '^') {
|
||||||
|
// negated char
|
||||||
|
set.add(CharRange.isNot(str.charAt(pos + 1)));
|
||||||
|
pos += 2;
|
||||||
|
} else {
|
||||||
|
// char
|
||||||
|
set.add(CharRange.is(str.charAt(pos)));
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Gets the internal set as an array of CharRange objects.</p>
|
||||||
|
*
|
||||||
|
* @return an array of immutable CharRange objects
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
// NOTE: This is no longer public as CharRange is no longer a public class.
|
||||||
|
// It may be replaced when CharSet moves to Range.
|
||||||
|
/*public*/ CharRange[] getCharRanges() {
|
||||||
|
return set.toArray(CharRange.EMPTY_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Does the {@code CharSet} contain the specified
|
||||||
|
* character {@code ch}.</p>
|
||||||
|
*
|
||||||
|
* @param ch the character to check for
|
||||||
|
* @return {@code true} if the set contains the characters
|
||||||
|
*/
|
||||||
|
public boolean contains(final char ch) {
|
||||||
|
synchronized(set) {
|
||||||
|
for (final CharRange range : set) {
|
||||||
|
if (range.contains(ch)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basics
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Compares two {@code CharSet} objects, returning true if they represent
|
||||||
|
* exactly the same set of characters defined in the same way.</p>
|
||||||
|
*
|
||||||
|
* <p>The two sets {@code abc} and {@code a-c} are <i>not</i>
|
||||||
|
* equal according to this method.</p>
|
||||||
|
*
|
||||||
|
* @param obj the object to compare to
|
||||||
|
* @return true if equal
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof CharSet)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final CharSet other = (CharSet) obj;
|
||||||
|
return set.equals(other.set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets a hash code compatible with the equals method.</p>
|
||||||
|
*
|
||||||
|
* @return a suitable hash code
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 89 + set.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets a string representation of the set.</p>
|
||||||
|
*
|
||||||
|
* @return string representation of the set
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return set.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
248
after/src/main/java/org/apache/commons/lang3/CharSetUtils.java
Normal file
248
after/src/main/java/org/apache/commons/lang3/CharSetUtils.java
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Operations on {@code CharSet} instances.</p>
|
||||||
|
*
|
||||||
|
* <p>This class handles {@code null} input gracefully.
|
||||||
|
* An exception will not be thrown for a {@code null} input.
|
||||||
|
* Each method documents its behavior in more detail.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @see CharSet
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class CharSetUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||||
|
* and identifies whether any of the characters are present in the specified string.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharSetUtils.containsAny(null, *) = false
|
||||||
|
* CharSetUtils.containsAny("", *) = false
|
||||||
|
* CharSetUtils.containsAny(*, null) = false
|
||||||
|
* CharSetUtils.containsAny(*, "") = false
|
||||||
|
* CharSetUtils.containsAny("hello", "k-p") = true
|
||||||
|
* CharSetUtils.containsAny("hello", "a-d") = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see CharSet#getInstance(java.lang.String...) for set-syntax.
|
||||||
|
* @param str String to look for characters in, may be null
|
||||||
|
* @param set String[] set of characters to identify, may be null
|
||||||
|
* @return whether or not the characters in the set are in the primary string
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static boolean containsAny(final String str, final String... set) {
|
||||||
|
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final CharSet chars = CharSet.getInstance(set);
|
||||||
|
for (final char c : str.toCharArray()) {
|
||||||
|
if (chars.contains(c)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||||
|
* and returns the number of characters present in the specified string.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharSetUtils.count(null, *) = 0
|
||||||
|
* CharSetUtils.count("", *) = 0
|
||||||
|
* CharSetUtils.count(*, null) = 0
|
||||||
|
* CharSetUtils.count(*, "") = 0
|
||||||
|
* CharSetUtils.count("hello", "k-p") = 3
|
||||||
|
* CharSetUtils.count("hello", "a-e") = 1
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see CharSet#getInstance(java.lang.String...) for set-syntax.
|
||||||
|
* @param str String to count characters in, may be null
|
||||||
|
* @param set String[] set of characters to count, may be null
|
||||||
|
* @return the character count, zero if null string input
|
||||||
|
*/
|
||||||
|
public static int count(final String str, final String... set) {
|
||||||
|
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final CharSet chars = CharSet.getInstance(set);
|
||||||
|
int count = 0;
|
||||||
|
for (final char c : str.toCharArray()) {
|
||||||
|
if (chars.contains(c)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not all the Strings in an array are
|
||||||
|
* empty or not.
|
||||||
|
*
|
||||||
|
* @param strings String[] whose elements are being checked for emptiness
|
||||||
|
* @return whether or not the String is empty
|
||||||
|
*/
|
||||||
|
private static boolean deepEmpty(final String[] strings) {
|
||||||
|
if (strings != null) {
|
||||||
|
for (final String s : strings) {
|
||||||
|
if (StringUtils.isNotEmpty(s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||||
|
* and deletes any of characters present in the specified string.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharSetUtils.delete(null, *) = null
|
||||||
|
* CharSetUtils.delete("", *) = ""
|
||||||
|
* CharSetUtils.delete(*, null) = *
|
||||||
|
* CharSetUtils.delete(*, "") = *
|
||||||
|
* CharSetUtils.delete("hello", "hl") = "eo"
|
||||||
|
* CharSetUtils.delete("hello", "le") = "ho"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see CharSet#getInstance(java.lang.String...) for set-syntax.
|
||||||
|
* @param str String to delete characters from, may be null
|
||||||
|
* @param set String[] set of characters to delete, may be null
|
||||||
|
* @return the modified String, {@code null} if null string input
|
||||||
|
*/
|
||||||
|
public static String delete(final String str, final String... set) {
|
||||||
|
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
return modify(str, set, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Takes an argument in set-syntax, see evaluateSet,
|
||||||
|
* and keeps any of characters present in the specified string.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharSetUtils.keep(null, *) = null
|
||||||
|
* CharSetUtils.keep("", *) = ""
|
||||||
|
* CharSetUtils.keep(*, null) = ""
|
||||||
|
* CharSetUtils.keep(*, "") = ""
|
||||||
|
* CharSetUtils.keep("hello", "hl") = "hll"
|
||||||
|
* CharSetUtils.keep("hello", "le") = "ell"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see CharSet#getInstance(java.lang.String...) for set-syntax.
|
||||||
|
* @param str String to keep characters from, may be null
|
||||||
|
* @param set String[] set of characters to keep, may be null
|
||||||
|
* @return the modified String, {@code null} if null string input
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static String keep(final String str, final String... set) {
|
||||||
|
if (str == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (str.isEmpty() || deepEmpty(set)) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
return modify(str, set, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of delete and keep
|
||||||
|
*
|
||||||
|
* @param str String to modify characters within
|
||||||
|
* @param set String[] set of characters to modify
|
||||||
|
* @param expect whether to evaluate on match, or non-match
|
||||||
|
* @return the modified String, not null
|
||||||
|
*/
|
||||||
|
private static String modify(final String str, final String[] set, final boolean expect) {
|
||||||
|
final CharSet chars = CharSet.getInstance(set);
|
||||||
|
final StringBuilder buffer = new StringBuilder(str.length());
|
||||||
|
final char[] chrs = str.toCharArray();
|
||||||
|
for (final char chr : chrs) {
|
||||||
|
if (chars.contains(chr) == expect) {
|
||||||
|
buffer.append(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Squeezes any repetitions of a character that is mentioned in the
|
||||||
|
* supplied set.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharSetUtils.squeeze(null, *) = null
|
||||||
|
* CharSetUtils.squeeze("", *) = ""
|
||||||
|
* CharSetUtils.squeeze(*, null) = *
|
||||||
|
* CharSetUtils.squeeze(*, "") = *
|
||||||
|
* CharSetUtils.squeeze("hello", "k-p") = "helo"
|
||||||
|
* CharSetUtils.squeeze("hello", "a-e") = "hello"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see CharSet#getInstance(java.lang.String...) for set-syntax.
|
||||||
|
* @param str the string to squeeze, may be null
|
||||||
|
* @param set the character set to use for manipulation, may be null
|
||||||
|
* @return the modified String, {@code null} if null string input
|
||||||
|
*/
|
||||||
|
public static String squeeze(final String str, final String... set) {
|
||||||
|
if (StringUtils.isEmpty(str) || deepEmpty(set)) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
final CharSet chars = CharSet.getInstance(set);
|
||||||
|
final StringBuilder buffer = new StringBuilder(str.length());
|
||||||
|
final char[] chrs = str.toCharArray();
|
||||||
|
final int sz = chrs.length;
|
||||||
|
char lastChar = chrs[0];
|
||||||
|
char ch = ' ';
|
||||||
|
Character inChars = null;
|
||||||
|
Character notInChars = null;
|
||||||
|
buffer.append(lastChar);
|
||||||
|
for (int i = 1; i < sz; i++) {
|
||||||
|
ch = chrs[i];
|
||||||
|
if (ch == lastChar) {
|
||||||
|
if (inChars != null && ch == inChars) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (notInChars == null || ch != notInChars) {
|
||||||
|
if (chars.contains(ch)) {
|
||||||
|
inChars = ch;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
notInChars = ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.append(ch);
|
||||||
|
lastChar = ch;
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CharSetUtils instances should NOT be constructed in standard programming.
|
||||||
|
* Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}.</p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||||
|
* to operate.</p>
|
||||||
|
*/
|
||||||
|
public CharSetUtils() {
|
||||||
|
}
|
||||||
|
}
|
551
after/src/main/java/org/apache/commons/lang3/CharUtils.java
Normal file
551
after/src/main/java/org/apache/commons/lang3/CharUtils.java
Normal file
|
@ -0,0 +1,551 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Operations on char primitives and Character objects.</p>
|
||||||
|
*
|
||||||
|
* <p>This class tries to handle {@code null} input gracefully.
|
||||||
|
* An exception will not be thrown for a {@code null} input.
|
||||||
|
* Each method documents its behavior in more detail.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public class CharUtils {
|
||||||
|
|
||||||
|
private static final String[] CHAR_STRING_ARRAY = new String[128];
|
||||||
|
|
||||||
|
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linefeed character LF ({@code '\n'}, Unicode 000a).
|
||||||
|
*
|
||||||
|
* @see <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
|
||||||
|
* for Character and String Literals</a>
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public static final char LF = '\n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carriage return characterf CR ('\r', Unicode 000d).
|
||||||
|
*
|
||||||
|
* @see <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
|
||||||
|
* for Character and String Literals</a>
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public static final char CR = '\r';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code \u0000} null control character ('\0'), abbreviated NUL.
|
||||||
|
*
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public static final char NUL = '\0';
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) {
|
||||||
|
CHAR_STRING_ARRAY[c] = String.valueOf(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code CharUtils} instances should NOT be constructed in standard programming.
|
||||||
|
* Instead, the class should be used as {@code CharUtils.toString('c');}.</p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||||
|
* to operate.</p>
|
||||||
|
*/
|
||||||
|
public CharUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to a Character.</p>
|
||||||
|
*
|
||||||
|
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||||
|
* same Character object each time.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toCharacterObject(' ') = ' '
|
||||||
|
* CharUtils.toCharacterObject('A') = 'A'
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches chars 0 through 127.
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @return a Character of the specified character
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Character toCharacterObject(final char ch) {
|
||||||
|
return Character.valueOf(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the String to a Character using the first character, returning
|
||||||
|
* null for empty Strings.</p>
|
||||||
|
*
|
||||||
|
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||||
|
* same Character object each time.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toCharacterObject(null) = null
|
||||||
|
* CharUtils.toCharacterObject("") = null
|
||||||
|
* CharUtils.toCharacterObject("A") = 'A'
|
||||||
|
* CharUtils.toCharacterObject("BA") = 'B'
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str the character to convert
|
||||||
|
* @return the Character value of the first letter of the String
|
||||||
|
*/
|
||||||
|
public static Character toCharacterObject(final String str) {
|
||||||
|
if (StringUtils.isEmpty(str)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Character.valueOf(str.charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Converts the Character to a char throwing an exception for {@code null}.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toChar(' ') = ' '
|
||||||
|
* CharUtils.toChar('A') = 'A'
|
||||||
|
* CharUtils.toChar(null) throws IllegalArgumentException
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @return the char value of the Character
|
||||||
|
* @throws NullPointerException if the Character is null
|
||||||
|
*/
|
||||||
|
public static char toChar(final Character ch) {
|
||||||
|
Validate.notNull(ch, "ch");
|
||||||
|
return ch.charValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the Character to a char handling {@code null}.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toChar(null, 'X') = 'X'
|
||||||
|
* CharUtils.toChar(' ', 'X') = ' '
|
||||||
|
* CharUtils.toChar('A', 'X') = 'A'
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @param defaultValue the value to use if the Character is null
|
||||||
|
* @return the char value of the Character or the default if null
|
||||||
|
*/
|
||||||
|
public static char toChar(final Character ch, final char defaultValue) {
|
||||||
|
if (ch == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return ch.charValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Converts the String to a char using the first character, throwing
|
||||||
|
* an exception on empty Strings.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toChar("A") = 'A'
|
||||||
|
* CharUtils.toChar("BA") = 'B'
|
||||||
|
* CharUtils.toChar(null) throws IllegalArgumentException
|
||||||
|
* CharUtils.toChar("") throws IllegalArgumentException
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str the character to convert
|
||||||
|
* @return the char value of the first letter of the String
|
||||||
|
* @throws NullPointerException if the string is null
|
||||||
|
* @throws IllegalArgumentException if the String is empty
|
||||||
|
*/
|
||||||
|
public static char toChar(final String str) {
|
||||||
|
Validate.notEmpty(str, "The String must not be empty");
|
||||||
|
return str.charAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the String to a char using the first character, defaulting
|
||||||
|
* the value on empty Strings.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toChar(null, 'X') = 'X'
|
||||||
|
* CharUtils.toChar("", 'X') = 'X'
|
||||||
|
* CharUtils.toChar("A", 'X') = 'A'
|
||||||
|
* CharUtils.toChar("BA", 'X') = 'B'
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str the character to convert
|
||||||
|
* @param defaultValue the value to use if the Character is null
|
||||||
|
* @return the char value of the first letter of the String or the default if null
|
||||||
|
*/
|
||||||
|
public static char toChar(final String str, final char defaultValue) {
|
||||||
|
if (StringUtils.isEmpty(str)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return str.charAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to the Integer it represents, throwing an
|
||||||
|
* exception if the character is not numeric.</p>
|
||||||
|
*
|
||||||
|
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toIntValue('3') = 3
|
||||||
|
* CharUtils.toIntValue('A') throws IllegalArgumentException
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @return the int value of the character
|
||||||
|
* @throws IllegalArgumentException if the character is not ASCII numeric
|
||||||
|
*/
|
||||||
|
public static int toIntValue(final char ch) {
|
||||||
|
if (!isAsciiNumeric(ch)) {
|
||||||
|
throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'");
|
||||||
|
}
|
||||||
|
return ch - 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to the Integer it represents, throwing an
|
||||||
|
* exception if the character is not numeric.</p>
|
||||||
|
*
|
||||||
|
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toIntValue('3', -1) = 3
|
||||||
|
* CharUtils.toIntValue('A', -1) = -1
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @param defaultValue the default value to use if the character is not numeric
|
||||||
|
* @return the int value of the character
|
||||||
|
*/
|
||||||
|
public static int toIntValue(final char ch, final int defaultValue) {
|
||||||
|
if (!isAsciiNumeric(ch)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return ch - 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to the Integer it represents, throwing an
|
||||||
|
* exception if the character is not numeric.</p>
|
||||||
|
*
|
||||||
|
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toIntValue('3') = 3
|
||||||
|
* CharUtils.toIntValue(null) throws IllegalArgumentException
|
||||||
|
* CharUtils.toIntValue('A') throws IllegalArgumentException
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert, not null
|
||||||
|
* @return the int value of the character
|
||||||
|
* @throws NullPointerException if the Character is null
|
||||||
|
* @throws IllegalArgumentException if the Character is not ASCII numeric
|
||||||
|
*/
|
||||||
|
public static int toIntValue(final Character ch) {
|
||||||
|
Validate.notNull(ch, "ch");
|
||||||
|
return toIntValue(ch.charValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to the Integer it represents, throwing an
|
||||||
|
* exception if the character is not numeric.</p>
|
||||||
|
*
|
||||||
|
* <p>This method converts the char '1' to the int 1 and so on.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toIntValue(null, -1) = -1
|
||||||
|
* CharUtils.toIntValue('3', -1) = 3
|
||||||
|
* CharUtils.toIntValue('A', -1) = -1
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @param defaultValue the default value to use if the character is not numeric
|
||||||
|
* @return the int value of the character
|
||||||
|
*/
|
||||||
|
public static int toIntValue(final Character ch, final int defaultValue) {
|
||||||
|
if (ch == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return toIntValue(ch.charValue(), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to a String that contains the one character.</p>
|
||||||
|
*
|
||||||
|
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||||
|
* same String object each time.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toString(' ') = " "
|
||||||
|
* CharUtils.toString('A') = "A"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @return a String containing the one specified character
|
||||||
|
*/
|
||||||
|
public static String toString(final char ch) {
|
||||||
|
if (ch < 128) {
|
||||||
|
return CHAR_STRING_ARRAY[ch];
|
||||||
|
}
|
||||||
|
return new String(new char[] {ch});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the character to a String that contains the one character.</p>
|
||||||
|
*
|
||||||
|
* <p>For ASCII 7 bit characters, this uses a cache that will return the
|
||||||
|
* same String object each time.</p>
|
||||||
|
*
|
||||||
|
* <p>If {@code null} is passed in, {@code null} will be returned.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.toString(null) = null
|
||||||
|
* CharUtils.toString(' ') = " "
|
||||||
|
* CharUtils.toString('A') = "A"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @return a String containing the one specified character
|
||||||
|
*/
|
||||||
|
public static String toString(final Character ch) {
|
||||||
|
if (ch == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return toString(ch.charValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Converts the string to the Unicode format '\u0020'.</p>
|
||||||
|
*
|
||||||
|
* <p>This format is the Java source code format.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.unicodeEscaped(' ') = "\u0020"
|
||||||
|
* CharUtils.unicodeEscaped('A') = "\u0041"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert
|
||||||
|
* @return the escaped Unicode string
|
||||||
|
*/
|
||||||
|
public static String unicodeEscaped(final char ch) {
|
||||||
|
return "\\u" +
|
||||||
|
HEX_DIGITS[(ch >> 12) & 15] +
|
||||||
|
HEX_DIGITS[(ch >> 8) & 15] +
|
||||||
|
HEX_DIGITS[(ch >> 4) & 15] +
|
||||||
|
HEX_DIGITS[(ch) & 15];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the string to the Unicode format '\u0020'.</p>
|
||||||
|
*
|
||||||
|
* <p>This format is the Java source code format.</p>
|
||||||
|
*
|
||||||
|
* <p>If {@code null} is passed in, {@code null} will be returned.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.unicodeEscaped(null) = null
|
||||||
|
* CharUtils.unicodeEscaped(' ') = "\u0020"
|
||||||
|
* CharUtils.unicodeEscaped('A') = "\u0041"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to convert, may be null
|
||||||
|
* @return the escaped Unicode string, null if null input
|
||||||
|
*/
|
||||||
|
public static String unicodeEscaped(final Character ch) {
|
||||||
|
if (ch == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return unicodeEscaped(ch.charValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAscii('a') = true
|
||||||
|
* CharUtils.isAscii('A') = true
|
||||||
|
* CharUtils.isAscii('3') = true
|
||||||
|
* CharUtils.isAscii('-') = true
|
||||||
|
* CharUtils.isAscii('\n') = true
|
||||||
|
* CharUtils.isAscii('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if less than 128
|
||||||
|
*/
|
||||||
|
public static boolean isAscii(final char ch) {
|
||||||
|
return ch < 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit printable.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiPrintable('a') = true
|
||||||
|
* CharUtils.isAsciiPrintable('A') = true
|
||||||
|
* CharUtils.isAsciiPrintable('3') = true
|
||||||
|
* CharUtils.isAsciiPrintable('-') = true
|
||||||
|
* CharUtils.isAsciiPrintable('\n') = false
|
||||||
|
* CharUtils.isAsciiPrintable('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if between 32 and 126 inclusive
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiPrintable(final char ch) {
|
||||||
|
return ch >= 32 && ch < 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit control.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiControl('a') = false
|
||||||
|
* CharUtils.isAsciiControl('A') = false
|
||||||
|
* CharUtils.isAsciiControl('3') = false
|
||||||
|
* CharUtils.isAsciiControl('-') = false
|
||||||
|
* CharUtils.isAsciiControl('\n') = true
|
||||||
|
* CharUtils.isAsciiControl('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if less than 32 or equals 127
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiControl(final char ch) {
|
||||||
|
return ch < 32 || ch == 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit alphabetic.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiAlpha('a') = true
|
||||||
|
* CharUtils.isAsciiAlpha('A') = true
|
||||||
|
* CharUtils.isAsciiAlpha('3') = false
|
||||||
|
* CharUtils.isAsciiAlpha('-') = false
|
||||||
|
* CharUtils.isAsciiAlpha('\n') = false
|
||||||
|
* CharUtils.isAsciiAlpha('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if between 65 and 90 or 97 and 122 inclusive
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiAlpha(final char ch) {
|
||||||
|
return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit alphabetic upper case.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiAlphaUpper('a') = false
|
||||||
|
* CharUtils.isAsciiAlphaUpper('A') = true
|
||||||
|
* CharUtils.isAsciiAlphaUpper('3') = false
|
||||||
|
* CharUtils.isAsciiAlphaUpper('-') = false
|
||||||
|
* CharUtils.isAsciiAlphaUpper('\n') = false
|
||||||
|
* CharUtils.isAsciiAlphaUpper('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if between 65 and 90 inclusive
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiAlphaUpper(final char ch) {
|
||||||
|
return ch >= 'A' && ch <= 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit alphabetic lower case.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiAlphaLower('a') = true
|
||||||
|
* CharUtils.isAsciiAlphaLower('A') = false
|
||||||
|
* CharUtils.isAsciiAlphaLower('3') = false
|
||||||
|
* CharUtils.isAsciiAlphaLower('-') = false
|
||||||
|
* CharUtils.isAsciiAlphaLower('\n') = false
|
||||||
|
* CharUtils.isAsciiAlphaLower('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if between 97 and 122 inclusive
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiAlphaLower(final char ch) {
|
||||||
|
return ch >= 'a' && ch <= 'z';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit numeric.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiNumeric('a') = false
|
||||||
|
* CharUtils.isAsciiNumeric('A') = false
|
||||||
|
* CharUtils.isAsciiNumeric('3') = true
|
||||||
|
* CharUtils.isAsciiNumeric('-') = false
|
||||||
|
* CharUtils.isAsciiNumeric('\n') = false
|
||||||
|
* CharUtils.isAsciiNumeric('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if between 48 and 57 inclusive
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiNumeric(final char ch) {
|
||||||
|
return ch >= '0' && ch <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the character is ASCII 7 bit numeric.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* CharUtils.isAsciiAlphanumeric('a') = true
|
||||||
|
* CharUtils.isAsciiAlphanumeric('A') = true
|
||||||
|
* CharUtils.isAsciiAlphanumeric('3') = true
|
||||||
|
* CharUtils.isAsciiAlphanumeric('-') = false
|
||||||
|
* CharUtils.isAsciiAlphanumeric('\n') = false
|
||||||
|
* CharUtils.isAsciiAlphanumeric('©') = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive
|
||||||
|
*/
|
||||||
|
public static boolean isAsciiAlphanumeric(final char ch) {
|
||||||
|
return isAsciiAlpha(ch) || isAsciiNumeric(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.</p>
|
||||||
|
*
|
||||||
|
* @param x the first {@code char} to compare
|
||||||
|
* @param y the second {@code char} to compare
|
||||||
|
* @return the value {@code 0} if {@code x == y};
|
||||||
|
* a value less than {@code 0} if {@code x < y}; and
|
||||||
|
* a value greater than {@code 0} if {@code x > y}
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public static int compare(final char x, final char y) {
|
||||||
|
return x-y;
|
||||||
|
}
|
||||||
|
}
|
69
after/src/main/java/org/apache/commons/lang3/Charsets.java
Normal file
69
after/src/main/java/org/apache/commons/lang3/Charsets.java
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only.
|
||||||
|
* <p>
|
||||||
|
* Provides utilities for {@link Charset}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Package private since Apache Commons IO already provides a Charsets because {@link Charset} is in
|
||||||
|
* {@code java.nio.charset}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
class Charsets {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given {@code charset} or the default Charset if {@code charset} is null.
|
||||||
|
*
|
||||||
|
* @param charset a Charset or null.
|
||||||
|
* @return the given {@code charset} or the default Charset if {@code charset} is null.
|
||||||
|
*/
|
||||||
|
static Charset toCharset(final Charset charset) {
|
||||||
|
return charset == null ? Charset.defaultCharset() : charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given {@code charset} or the default Charset if {@code charset} is null.
|
||||||
|
*
|
||||||
|
* @param charsetName a Charset or null.
|
||||||
|
* @return the given {@code charset} or the default Charset if {@code charset} is null.
|
||||||
|
* @throws UnsupportedCharsetException If no support for the named charset is available in this instance of the Java
|
||||||
|
* virtual machine
|
||||||
|
*/
|
||||||
|
static Charset toCharset(final String charsetName) {
|
||||||
|
return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given {@code charset} or the default Charset if {@code charset} is null.
|
||||||
|
*
|
||||||
|
* @param charsetName a Charset or null.
|
||||||
|
* @return the given {@code charset} or the default Charset if {@code charset} is null.
|
||||||
|
*/
|
||||||
|
static String toCharsetName(final String charsetName) {
|
||||||
|
return charsetName == null ? Charset.defaultCharset().name() : charsetName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helps work with {@link ClassLoader}.
|
||||||
|
*
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public class ClassLoaderUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given class loader to a String calling {@link #toString(URLClassLoader)}.
|
||||||
|
*
|
||||||
|
* @param classLoader to URLClassLoader to convert.
|
||||||
|
* @return the formatted string.
|
||||||
|
*/
|
||||||
|
public static String toString(final ClassLoader classLoader) {
|
||||||
|
if (classLoader instanceof URLClassLoader) {
|
||||||
|
return toString((URLClassLoader) classLoader);
|
||||||
|
}
|
||||||
|
return classLoader.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given URLClassLoader to a String in the format
|
||||||
|
* {@code "URLClassLoader.toString() + [URL1, URL2, ...]"}.
|
||||||
|
*
|
||||||
|
* @param classLoader to URLClassLoader to convert.
|
||||||
|
* @return the formatted string.
|
||||||
|
*/
|
||||||
|
public static String toString(final URLClassLoader classLoader) {
|
||||||
|
return classLoader + Arrays.toString(classLoader.getURLs());
|
||||||
|
}
|
||||||
|
}
|
129
after/src/main/java/org/apache/commons/lang3/ClassPathUtils.java
Normal file
129
after/src/main/java/org/apache/commons/lang3/ClassPathUtils.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1562
after/src/main/java/org/apache/commons/lang3/ClassUtils.java
Normal file
1562
after/src/main/java/org/apache/commons/lang3/ClassUtils.java
Normal file
File diff suppressed because it is too large
Load diff
1563
after/src/main/java/org/apache/commons/lang3/Conversion.java
Normal file
1563
after/src/main/java/org/apache/commons/lang3/Conversion.java
Normal file
File diff suppressed because it is too large
Load diff
379
after/src/main/java/org/apache/commons/lang3/EnumUtils.java
Normal file
379
after/src/main/java/org/apache/commons/lang3/EnumUtils.java
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Utility library to provide helper methods for Java enums.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class EnumUtils {
|
||||||
|
|
||||||
|
private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted";
|
||||||
|
private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits";
|
||||||
|
private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type";
|
||||||
|
private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate {@code enumClass}.
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass to check
|
||||||
|
* @return {@code enumClass}
|
||||||
|
* @throws NullPointerException if {@code enumClass} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
private static <E extends Enum<E>> Class<E> asEnum(final Class<E> enumClass) {
|
||||||
|
Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED);
|
||||||
|
Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass);
|
||||||
|
return enumClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that {@code enumClass} is compatible with representation in a {@code long}.
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass to check
|
||||||
|
* @return {@code enumClass}
|
||||||
|
* @throws NullPointerException if {@code enumClass} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
|
||||||
|
* @since 3.0.1
|
||||||
|
*/
|
||||||
|
private static <E extends Enum<E>> Class<E> checkBitVectorable(final Class<E> enumClass) {
|
||||||
|
final E[] constants = asEnum(enumClass).getEnumConstants();
|
||||||
|
Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS,
|
||||||
|
Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE));
|
||||||
|
|
||||||
|
return enumClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a long bit vector representation of the given array of Enum values.</p>
|
||||||
|
*
|
||||||
|
* <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
|
||||||
|
*
|
||||||
|
* <p>Do not use this method if you have more than 64 values in your Enum, as this
|
||||||
|
* would create a value greater than a long can hold.</p>
|
||||||
|
*
|
||||||
|
* @param enumClass the class of the enum we are working with, not {@code null}
|
||||||
|
* @param values the values we want to convert, not {@code null}
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @return a long whose value provides a binary representation of the given set of enum values.
|
||||||
|
* @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
|
||||||
|
* @since 3.0.1
|
||||||
|
* @see #generateBitVectors(Class, Iterable)
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final E... values) {
|
||||||
|
Validate.noNullElements(values);
|
||||||
|
return generateBitVector(enumClass, Arrays.asList(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a long bit vector representation of the given subset of an Enum.</p>
|
||||||
|
*
|
||||||
|
* <p>This generates a value that is usable by {@link EnumUtils#processBitVector}.</p>
|
||||||
|
*
|
||||||
|
* <p>Do not use this method if you have more than 64 values in your Enum, as this
|
||||||
|
* would create a value greater than a long can hold.</p>
|
||||||
|
*
|
||||||
|
* @param enumClass the class of the enum we are working with, not {@code null}
|
||||||
|
* @param values the values we want to convert, not {@code null}, neither containing {@code null}
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @return a long whose value provides a binary representation of the given set of enum values.
|
||||||
|
* @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values,
|
||||||
|
* or if any {@code values} {@code null}
|
||||||
|
* @since 3.0.1
|
||||||
|
* @see #generateBitVectors(Class, Iterable)
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> long generateBitVector(final Class<E> enumClass, final Iterable<? extends E> values) {
|
||||||
|
checkBitVectorable(enumClass);
|
||||||
|
Validate.notNull(values);
|
||||||
|
long total = 0;
|
||||||
|
for (final E constant : values) {
|
||||||
|
Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
|
||||||
|
total |= 1L << constant.ordinal();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
|
||||||
|
*
|
||||||
|
* <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
|
||||||
|
*
|
||||||
|
* <p>Use this method if you have more than 64 values in your Enum.</p>
|
||||||
|
*
|
||||||
|
* @param enumClass the class of the enum we are working with, not {@code null}
|
||||||
|
* @param values the values we want to convert, not {@code null}, neither containing {@code null}
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @return a long[] whose values provide a binary representation of the given set of enum values
|
||||||
|
* with least significant digits rightmost.
|
||||||
|
* @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final E... values) {
|
||||||
|
asEnum(enumClass);
|
||||||
|
Validate.noNullElements(values);
|
||||||
|
final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
|
||||||
|
Collections.addAll(condensed, values);
|
||||||
|
final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
|
||||||
|
for (final E value : condensed) {
|
||||||
|
result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE);
|
||||||
|
}
|
||||||
|
ArrayUtils.reverse(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.</p>
|
||||||
|
*
|
||||||
|
* <p>This generates a value that is usable by {@link EnumUtils#processBitVectors}.</p>
|
||||||
|
*
|
||||||
|
* <p>Use this method if you have more than 64 values in your Enum.</p>
|
||||||
|
*
|
||||||
|
* @param enumClass the class of the enum we are working with, not {@code null}
|
||||||
|
* @param values the values we want to convert, not {@code null}, neither containing {@code null}
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @return a long[] whose values provide a binary representation of the given set of enum values
|
||||||
|
* with least significant digits rightmost.
|
||||||
|
* @throws NullPointerException if {@code enumClass} or {@code values} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null}
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> long[] generateBitVectors(final Class<E> enumClass, final Iterable<? extends E> values) {
|
||||||
|
asEnum(enumClass);
|
||||||
|
Validate.notNull(values);
|
||||||
|
final EnumSet<E> condensed = EnumSet.noneOf(enumClass);
|
||||||
|
for (final E constant : values) {
|
||||||
|
Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED);
|
||||||
|
condensed.add(constant);
|
||||||
|
}
|
||||||
|
final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1];
|
||||||
|
for (final E value : condensed) {
|
||||||
|
result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE);
|
||||||
|
}
|
||||||
|
ArrayUtils.reverse(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the enum for the class, returning {@code null} if not found.</p>
|
||||||
|
*
|
||||||
|
* <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
|
||||||
|
* for an invalid enum name.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @param enumName the enum name, null returns null
|
||||||
|
* @return the enum, null if not found
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName) {
|
||||||
|
return getEnum(enumClass, enumName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the enum for the class, returning {@code defaultEnum} if not found.</p>
|
||||||
|
*
|
||||||
|
* <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
|
||||||
|
* for an invalid enum name.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @param enumName the enum name, null returns default enum
|
||||||
|
* @param defaultEnum the default enum
|
||||||
|
* @return the enum, default enum if not found
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName, final E defaultEnum) {
|
||||||
|
if (enumName == null) {
|
||||||
|
return defaultEnum;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Enum.valueOf(enumClass, enumName);
|
||||||
|
} catch (final IllegalArgumentException ex) {
|
||||||
|
return defaultEnum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the enum for the class, returning {@code null} if not found.</p>
|
||||||
|
*
|
||||||
|
* <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
|
||||||
|
* for an invalid enum name and performs case insensitive matching of the name.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @param enumName the enum name, null returns null
|
||||||
|
* @return the enum, null if not found
|
||||||
|
* @since 3.8
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
|
||||||
|
return getEnumIgnoreCase(enumClass, enumName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the enum for the class, returning {@code defaultEnum} if not found.</p>
|
||||||
|
*
|
||||||
|
* <p>This method differs from {@link Enum#valueOf} in that it does not throw an exception
|
||||||
|
* for an invalid enum name and performs case insensitive matching of the name.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @param enumName the enum name, null returns default enum
|
||||||
|
* @param defaultEnum the default enum
|
||||||
|
* @return the enum, default enum if not found
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E getEnumIgnoreCase(final Class<E> enumClass, final String enumName, final E defaultEnum) {
|
||||||
|
if (enumName == null || !enumClass.isEnum()) {
|
||||||
|
return defaultEnum;
|
||||||
|
}
|
||||||
|
for (final E each : enumClass.getEnumConstants()) {
|
||||||
|
if (each.name().equalsIgnoreCase(enumName)) {
|
||||||
|
return each;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the {@code List} of enums.</p>
|
||||||
|
*
|
||||||
|
* <p>This method is useful when you need a list of enums rather than an array.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @return the modifiable list of enums, never null
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> List<E> getEnumList(final Class<E> enumClass) {
|
||||||
|
return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the {@code Map} of enums by name.</p>
|
||||||
|
*
|
||||||
|
* <p>This method is useful when you need a map of enums by name.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @return the modifiable map of enum names to enums, never null
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> Map<String, E> getEnumMap(final Class<E> enumClass) {
|
||||||
|
final Map<String, E> map = new LinkedHashMap<>();
|
||||||
|
for (final E e: enumClass.getEnumConstants()) {
|
||||||
|
map.put(e.name(), e);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if the specified name is a valid enum for the class.</p>
|
||||||
|
*
|
||||||
|
* <p>This method differs from {@link Enum#valueOf} in that checks if the name is
|
||||||
|
* a valid enum without needing to catch the exception.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @param enumName the enum name, null returns false
|
||||||
|
* @return true if the enum name is valid, otherwise false
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> boolean isValidEnum(final Class<E> enumClass, final String enumName) {
|
||||||
|
return getEnum(enumClass, enumName) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if the specified name is a valid enum for the class.</p>
|
||||||
|
*
|
||||||
|
* <p>This method differs from {@link Enum#valueOf} in that checks if the name is
|
||||||
|
* a valid enum without needing to catch the exception
|
||||||
|
* and performs case insensitive matching of the name.</p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @param enumClass the class of the enum to query, not null
|
||||||
|
* @param enumName the enum name, null returns false
|
||||||
|
* @return true if the enum name is valid, otherwise false
|
||||||
|
* @since 3.8
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> boolean isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName) {
|
||||||
|
return getEnumIgnoreCase(enumClass, enumName) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Convert a long value created by {@link EnumUtils#generateBitVector} into the set of
|
||||||
|
* enum values that it represents.</p>
|
||||||
|
*
|
||||||
|
* <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
|
||||||
|
* @param enumClass the class of the enum we are working with, not {@code null}
|
||||||
|
* @param value the long value representation of a set of enum values
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @return a set of enum values
|
||||||
|
* @throws NullPointerException if {@code enumClass} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values
|
||||||
|
* @since 3.0.1
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> EnumSet<E> processBitVector(final Class<E> enumClass, final long value) {
|
||||||
|
checkBitVectorable(enumClass).getEnumConstants();
|
||||||
|
return processBitVectors(enumClass, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of
|
||||||
|
* enum values that it represents.</p>
|
||||||
|
*
|
||||||
|
* <p>If you store this value, beware any changes to the enum that would affect ordinal values.</p>
|
||||||
|
* @param enumClass the class of the enum we are working with, not {@code null}
|
||||||
|
* @param values the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null}
|
||||||
|
* @param <E> the type of the enumeration
|
||||||
|
* @return a set of enum values
|
||||||
|
* @throws NullPointerException if {@code enumClass} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code enumClass} is not an enum class
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> EnumSet<E> processBitVectors(final Class<E> enumClass, final long... values) {
|
||||||
|
final EnumSet<E> results = EnumSet.noneOf(asEnum(enumClass));
|
||||||
|
final long[] lvalues = ArrayUtils.clone(Validate.notNull(values));
|
||||||
|
ArrayUtils.reverse(lvalues);
|
||||||
|
for (final E constant : enumClass.getEnumConstants()) {
|
||||||
|
final int block = constant.ordinal() / Long.SIZE;
|
||||||
|
if (block < lvalues.length && (lvalues[block] & 1L << (constant.ordinal() % Long.SIZE)) != 0) {
|
||||||
|
results.add(constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor is public to permit tools that require a JavaBean
|
||||||
|
* instance to operate.
|
||||||
|
*/
|
||||||
|
public EnumUtils() {
|
||||||
|
}
|
||||||
|
}
|
693
after/src/main/java/org/apache/commons/lang3/Functions.java
Normal file
693
after/src/main/java/org/apache/commons/lang3/Functions.java
Normal file
|
@ -0,0 +1,693 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Streams.FailableStream;
|
||||||
|
import org.apache.commons.lang3.function.FailableBooleanSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides utility functions, and classes for working with the {@code java.util.function} package, or more
|
||||||
|
* generally, with Java 8 lambdas. More specifically, it attempts to address the fact that lambdas are supposed not to
|
||||||
|
* throw Exceptions, at least not checked Exceptions, AKA instances of {@link Exception}. This enforces the use of
|
||||||
|
* constructs like:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* Consumer<java.lang.reflect.Method> consumer = m -> {
|
||||||
|
* try {
|
||||||
|
* m.invoke(o, args);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* throw Functions.rethrow(t);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By replacing a {@link java.util.function.Consumer Consumer<O>} with a {@link FailableConsumer
|
||||||
|
* FailableConsumer<O,? extends Throwable>}, this can be written like follows:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* Functions.accept((m) -> m.invoke(o,args));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Obviously, the second version is much more concise and the spirit of Lambda expressions is met better than the second
|
||||||
|
* version.
|
||||||
|
* </p>
|
||||||
|
* @since 3.9
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.Failable}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class Functions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link BiConsumer} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <O1> Consumed type 1.
|
||||||
|
* @param <O2> Consumed type 2.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableBiConsumer}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableBiConsumer<O1, O2, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts the consumer.
|
||||||
|
*
|
||||||
|
* @param object1 the first parameter for the consumable to accept
|
||||||
|
* @param object2 the second parameter for the consumable to accept
|
||||||
|
* @throws T Thrown when the consumer fails.
|
||||||
|
*/
|
||||||
|
void accept(O1 object1, O2 object2) throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link BiFunction} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <O1> Input type 1.
|
||||||
|
* @param <O2> Input type 2.
|
||||||
|
* @param <R> Return type.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableBiFunction}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableBiFunction<O1, O2, R, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies this function.
|
||||||
|
*
|
||||||
|
* @param input1 the first input for the function
|
||||||
|
* @param input2 the second input for the function
|
||||||
|
* @return the result of the function
|
||||||
|
* @throws T Thrown when the function fails.
|
||||||
|
*/
|
||||||
|
R apply(O1 input1, O2 input2) throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link BiPredicate} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <O1> Predicate type 1.
|
||||||
|
* @param <O2> Predicate type 2.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableBiPredicate}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableBiPredicate<O1, O2, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the predicate.
|
||||||
|
*
|
||||||
|
* @param object1 the first object to test the predicate on
|
||||||
|
* @param object2 the second object to test the predicate on
|
||||||
|
* @return the predicate's evaluation
|
||||||
|
* @throws T if the predicate fails
|
||||||
|
*/
|
||||||
|
boolean test(O1 object1, O2 object2) throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link java.util.concurrent.Callable} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <R> Return type.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableCallable}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableCallable<R, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the callable.
|
||||||
|
*
|
||||||
|
* @return The value returned from the callable
|
||||||
|
* @throws T if the callable fails
|
||||||
|
*/
|
||||||
|
R call() throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link Consumer} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <O> Consumed type 1.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableConsumer}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableConsumer<O, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts the consumer.
|
||||||
|
*
|
||||||
|
* @param object the parameter for the consumable to accept
|
||||||
|
* @throws T Thrown when the consumer fails.
|
||||||
|
*/
|
||||||
|
void accept(O object) throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link Function} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <I> Input type 1.
|
||||||
|
* @param <R> Return type.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableFunction}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableFunction<I, R, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies this function.
|
||||||
|
*
|
||||||
|
* @param input the input for the function
|
||||||
|
* @return the result of the function
|
||||||
|
* @throws T Thrown when the function fails.
|
||||||
|
*/
|
||||||
|
R apply(I input) throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link Predicate} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <I> Predicate type 1.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailablePredicate}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailablePredicate<I, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the predicate.
|
||||||
|
*
|
||||||
|
* @param object the object to test the predicate on
|
||||||
|
* @return the predicate's evaluation
|
||||||
|
* @throws T if the predicate fails
|
||||||
|
*/
|
||||||
|
boolean test(I object) throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link Runnable} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableRunnable}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableRunnable<T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the function.
|
||||||
|
*
|
||||||
|
* @throws T Thrown when the function fails.
|
||||||
|
*/
|
||||||
|
void run() throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface like {@link Supplier} that declares a {@code Throwable}.
|
||||||
|
*
|
||||||
|
* <p>TODO for 4.0: Move to org.apache.commons.lang3.function.</p>
|
||||||
|
*
|
||||||
|
* @param <R> Return type.
|
||||||
|
* @param <T> Thrown exception.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.function.FailableSupplier}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FailableSupplier<R, T extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplies an object
|
||||||
|
*
|
||||||
|
* @return a result
|
||||||
|
* @throws T if the supplier fails
|
||||||
|
*/
|
||||||
|
R get() throws T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes a consumer and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer to consume
|
||||||
|
* @param object1 the first object to consume by {@code consumer}
|
||||||
|
* @param object2 the second object to consume by {@code consumer}
|
||||||
|
* @param <O1> the type of the first argument the consumer accepts
|
||||||
|
* @param <O2> the type of the second argument the consumer accepts
|
||||||
|
* @param <T> the type of checked exception the consumer may throw
|
||||||
|
*/
|
||||||
|
public static <O1, O2, T extends Throwable> void accept(final FailableBiConsumer<O1, O2, T> consumer,
|
||||||
|
final O1 object1, final O2 object2) {
|
||||||
|
run(() -> consumer.accept(object1, object2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes a consumer and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer to consume
|
||||||
|
* @param object the object to consume by {@code consumer}
|
||||||
|
* @param <O> the type the consumer accepts
|
||||||
|
* @param <T> the type of checked exception the consumer may throw
|
||||||
|
*/
|
||||||
|
public static <O, T extends Throwable> void accept(final FailableConsumer<O, T> consumer, final O object) {
|
||||||
|
run(() -> consumer.accept(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a function and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param function the function to apply
|
||||||
|
* @param input1 the first input to apply {@code function} on
|
||||||
|
* @param input2 the second input to apply {@code function} on
|
||||||
|
* @param <O1> the type of the first argument the function accepts
|
||||||
|
* @param <O2> the type of the second argument the function accepts
|
||||||
|
* @param <O> the return type of the function
|
||||||
|
* @param <T> the type of checked exception the function may throw
|
||||||
|
* @return the value returned from the function
|
||||||
|
*/
|
||||||
|
public static <O1, O2, O, T extends Throwable> O apply(final FailableBiFunction<O1, O2, O, T> function,
|
||||||
|
final O1 input1, final O2 input2) {
|
||||||
|
return get(() -> function.apply(input1, input2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a function and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param function the function to apply
|
||||||
|
* @param input the input to apply {@code function} on
|
||||||
|
* @param <I> the type of the argument the function accepts
|
||||||
|
* @param <O> the return type of the function
|
||||||
|
* @param <T> the type of checked exception the function may throw
|
||||||
|
* @return the value returned from the function
|
||||||
|
*/
|
||||||
|
public static <I, O, T extends Throwable> O apply(final FailableFunction<I, O, T> function, final I input) {
|
||||||
|
return get(() -> function.apply(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableBiConsumer} into a standard {@link BiConsumer}.
|
||||||
|
*
|
||||||
|
* @param <O1> the type of the first argument of the consumers
|
||||||
|
* @param <O2> the type of the second argument of the consumers
|
||||||
|
* @param consumer a failable {@code BiConsumer}
|
||||||
|
* @return a standard {@code BiConsumer}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O1, O2> BiConsumer<O1, O2> asBiConsumer(final FailableBiConsumer<O1, O2, ?> consumer) {
|
||||||
|
return (input1, input2) -> accept(consumer, input1, input2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableBiFunction} into a standard {@link BiFunction}.
|
||||||
|
*
|
||||||
|
* @param <O1> the type of the first argument of the input of the functions
|
||||||
|
* @param <O2> the type of the second argument of the input of the functions
|
||||||
|
* @param <O> the type of the output of the functions
|
||||||
|
* @param function a {@code FailableBiFunction}
|
||||||
|
* @return a standard {@code BiFunction}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O1, O2, O> BiFunction<O1, O2, O> asBiFunction(final FailableBiFunction<O1, O2, O, ?> function) {
|
||||||
|
return (input1, input2) -> apply(function, input1, input2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableBiPredicate} into a standard {@link BiPredicate}.
|
||||||
|
*
|
||||||
|
* @param <O1> the type of the first argument used by the predicates
|
||||||
|
* @param <O2> the type of the second argument used by the predicates
|
||||||
|
* @param predicate a {@code FailableBiPredicate}
|
||||||
|
* @return a standard {@code BiPredicate}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O1, O2> BiPredicate<O1, O2> asBiPredicate(final FailableBiPredicate<O1, O2, ?> predicate) {
|
||||||
|
return (input1, input2) -> test(predicate, input1, input2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableCallable} into a standard {@link Callable}.
|
||||||
|
*
|
||||||
|
* @param <O> the type used by the callables
|
||||||
|
* @param callable a {@code FailableCallable}
|
||||||
|
* @return a standard {@code Callable}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O> Callable<O> asCallable(final FailableCallable<O, ?> callable) {
|
||||||
|
return () -> call(callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableConsumer} into a standard {@link Consumer}.
|
||||||
|
*
|
||||||
|
* @param <I> the type used by the consumers
|
||||||
|
* @param consumer a {@code FailableConsumer}
|
||||||
|
* @return a standard {@code Consumer}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <I> Consumer<I> asConsumer(final FailableConsumer<I, ?> consumer) {
|
||||||
|
return input -> accept(consumer, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableFunction} into a standard {@link Function}.
|
||||||
|
*
|
||||||
|
* @param <I> the type of the input of the functions
|
||||||
|
* @param <O> the type of the output of the functions
|
||||||
|
* @param function a {code FailableFunction}
|
||||||
|
* @return a standard {@code Function}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <I, O> Function<I, O> asFunction(final FailableFunction<I, O, ?> function) {
|
||||||
|
return input -> apply(function, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailablePredicate} into a standard {@link Predicate}.
|
||||||
|
*
|
||||||
|
* @param <I> the type used by the predicates
|
||||||
|
* @param predicate a {@code FailablePredicate}
|
||||||
|
* @return a standard {@code Predicate}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <I> Predicate<I> asPredicate(final FailablePredicate<I, ?> predicate) {
|
||||||
|
return input -> test(predicate, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableRunnable} into a standard {@link Runnable}.
|
||||||
|
*
|
||||||
|
* @param runnable a {@code FailableRunnable}
|
||||||
|
* @return a standard {@code Runnable}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static Runnable asRunnable(final FailableRunnable<?> runnable) {
|
||||||
|
return () -> run(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link FailableSupplier} into a standard {@link Supplier}.
|
||||||
|
*
|
||||||
|
* @param <O> the type supplied by the suppliers
|
||||||
|
* @param supplier a {@code FailableSupplier}
|
||||||
|
* @return a standard {@code Supplier}
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O> Supplier<O> asSupplier(final FailableSupplier<O, ?> supplier) {
|
||||||
|
return () -> get(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a callable and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param callable the callable to call
|
||||||
|
* @param <O> the return type of the callable
|
||||||
|
* @param <T> the type of checked exception the callable may throw
|
||||||
|
* @return the value returned from the callable
|
||||||
|
*/
|
||||||
|
public static <O, T extends Throwable> O call(final FailableCallable<O, T> callable) {
|
||||||
|
return get(callable::call);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes a supplier, and returns the result.
|
||||||
|
*
|
||||||
|
* @param supplier The supplier to invoke.
|
||||||
|
* @param <O> The suppliers output type.
|
||||||
|
* @param <T> The type of checked exception, which the supplier can throw.
|
||||||
|
* @return The object, which has been created by the supplier
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O, T extends Throwable> O get(final FailableSupplier<O, T> supplier) {
|
||||||
|
try {
|
||||||
|
return supplier.get();
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw rethrow(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes a boolean supplier, and returns the result.
|
||||||
|
*
|
||||||
|
* @param supplier The boolean supplier to invoke.
|
||||||
|
* @param <T> The type of checked exception, which the supplier can throw.
|
||||||
|
* @return The boolean, which has been created by the supplier
|
||||||
|
*/
|
||||||
|
private static <T extends Throwable> boolean getAsBoolean(final FailableBooleanSupplier<T> supplier) {
|
||||||
|
try {
|
||||||
|
return supplier.getAsBoolean();
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw rethrow(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Rethrows a {@link Throwable} as an unchecked exception. If the argument is already unchecked, namely a
|
||||||
|
* {@code RuntimeException} or {@code Error} then the argument will be rethrown without modification. If the
|
||||||
|
* exception is {@code IOException} then it will be wrapped into a {@code UncheckedIOException}. In every other
|
||||||
|
* cases the exception will be wrapped into a {@code
|
||||||
|
* UndeclaredThrowableException}
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that there is a declared return type for this method, even though it never returns. The reason for that is
|
||||||
|
* to support the usual pattern:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* throw rethrow(myUncheckedException);</pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* instead of just calling the method. This pattern may help the Java compiler to recognize that at that point an
|
||||||
|
* exception will be thrown and the code flow analysis will not demand otherwise mandatory commands that could
|
||||||
|
* follow the method call, like a {@code return} statement from a value returning method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param throwable The throwable to rethrow ossibly wrapped into an unchecked exception
|
||||||
|
* @return Never returns anything, this method never terminates normally.
|
||||||
|
*/
|
||||||
|
public static RuntimeException rethrow(final Throwable throwable) {
|
||||||
|
Objects.requireNonNull(throwable, "throwable");
|
||||||
|
if (throwable instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) throwable;
|
||||||
|
} else if (throwable instanceof Error) {
|
||||||
|
throw (Error) throwable;
|
||||||
|
} else if (throwable instanceof IOException) {
|
||||||
|
throw new UncheckedIOException((IOException) throwable);
|
||||||
|
} else {
|
||||||
|
throw new UndeclaredThrowableException(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a runnable and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param runnable The runnable to run
|
||||||
|
* @param <T> the type of checked exception the runnable may throw
|
||||||
|
*/
|
||||||
|
public static <T extends Throwable> void run(final FailableRunnable<T> runnable) {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw rethrow(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given collection into a {@link FailableStream}. The {@link FailableStream} consists of the
|
||||||
|
* collections elements. Shortcut for
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Functions.stream(collection.stream());</pre>
|
||||||
|
*
|
||||||
|
* @param collection The collection, which is being converted into a {@link FailableStream}.
|
||||||
|
* @param <O> The collections element type. (In turn, the result streams element type.)
|
||||||
|
* @return The created {@link FailableStream}.
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O> FailableStream<O> stream(final Collection<O> collection) {
|
||||||
|
return new FailableStream<>(collection.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given stream into a {@link FailableStream}. The {@link FailableStream} consists of the same
|
||||||
|
* elements, than the input stream. However, failable lambdas, like {@link FailablePredicate},
|
||||||
|
* {@link FailableFunction}, and {@link FailableConsumer} may be applied, rather than {@link Predicate},
|
||||||
|
* {@link Function}, {@link Consumer}, etc.
|
||||||
|
*
|
||||||
|
* @param stream The stream, which is being converted into a {@link FailableStream}.
|
||||||
|
* @param <O> The streams element type.
|
||||||
|
* @return The created {@link FailableStream}.
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public static <O> FailableStream<O> stream(final Stream<O> stream) {
|
||||||
|
return new FailableStream<>(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests a predicate and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param predicate the predicate to test
|
||||||
|
* @param object1 the first input to test by {@code predicate}
|
||||||
|
* @param object2 the second input to test by {@code predicate}
|
||||||
|
* @param <O1> the type of the first argument the predicate tests
|
||||||
|
* @param <O2> the type of the second argument the predicate tests
|
||||||
|
* @param <T> the type of checked exception the predicate may throw
|
||||||
|
* @return the boolean value returned by the predicate
|
||||||
|
*/
|
||||||
|
public static <O1, O2, T extends Throwable> boolean test(final FailableBiPredicate<O1, O2, T> predicate,
|
||||||
|
final O1 object1, final O2 object2) {
|
||||||
|
return getAsBoolean(() -> predicate.test(object1, object2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests a predicate and rethrows any exception as a {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param predicate the predicate to test
|
||||||
|
* @param object the input to test by {@code predicate}
|
||||||
|
* @param <O> the type of argument the predicate tests
|
||||||
|
* @param <T> the type of checked exception the predicate may throw
|
||||||
|
* @return the boolean value returned by the predicate
|
||||||
|
*/
|
||||||
|
public static <O, T extends Throwable> boolean test(final FailablePredicate<O, T> predicate, final O object) {
|
||||||
|
return getAsBoolean(() -> predicate.test(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple try-with-resources implementation, that can be used, if your objects do not implement the
|
||||||
|
* {@link AutoCloseable} interface. The method executes the {@code action}. The method guarantees, that <em>all</em>
|
||||||
|
* the {@code resources} are being executed, in the given order, afterwards, and regardless of success, or failure.
|
||||||
|
* If either the original action, or any of the resource action fails, then the <em>first</em> failure (AKA
|
||||||
|
* {@link Throwable} is rethrown. Example use:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* final FileInputStream fis = new FileInputStream("my.file");
|
||||||
|
* Functions.tryWithResources(useInputStream(fis), null, () -> fis.close());
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param action The action to execute. This object <em>will</em> always be invoked.
|
||||||
|
* @param errorHandler An optional error handler, which will be invoked finally, if any error occurred. The error
|
||||||
|
* handler will receive the first error, AKA {@link Throwable}.
|
||||||
|
* @param resources The resource actions to execute. <em>All</em> resource actions will be invoked, in the given
|
||||||
|
* order. A resource action is an instance of {@link FailableRunnable}, which will be executed.
|
||||||
|
* @see #tryWithResources(FailableRunnable, FailableRunnable...)
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static void tryWithResources(final FailableRunnable<? extends Throwable> action,
|
||||||
|
final FailableConsumer<Throwable, ? extends Throwable> errorHandler,
|
||||||
|
final FailableRunnable<? extends Throwable>... resources) {
|
||||||
|
final FailableConsumer<Throwable, ? extends Throwable> actualErrorHandler;
|
||||||
|
if (errorHandler == null) {
|
||||||
|
actualErrorHandler = Functions::rethrow;
|
||||||
|
} else {
|
||||||
|
actualErrorHandler = errorHandler;
|
||||||
|
}
|
||||||
|
if (resources != null) {
|
||||||
|
for (final FailableRunnable<? extends Throwable> failableRunnable : resources) {
|
||||||
|
Objects.requireNonNull(failableRunnable, "runnable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Throwable th = null;
|
||||||
|
try {
|
||||||
|
action.run();
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
th = t;
|
||||||
|
}
|
||||||
|
if (resources != null) {
|
||||||
|
for (final FailableRunnable<?> runnable : resources) {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
if (th == null) {
|
||||||
|
th = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (th != null) {
|
||||||
|
try {
|
||||||
|
actualErrorHandler.accept(th);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw rethrow(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple try-with-resources implementation, that can be used, if your objects do not implement the
|
||||||
|
* {@link AutoCloseable} interface. The method executes the {@code action}. The method guarantees, that <em>all</em>
|
||||||
|
* the {@code resources} are being executed, in the given order, afterwards, and regardless of success, or failure.
|
||||||
|
* If either the original action, or any of the resource action fails, then the <em>first</em> failure (AKA
|
||||||
|
* {@link Throwable} is rethrown. Example use:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* final FileInputStream fis = new FileInputStream("my.file");
|
||||||
|
* Functions.tryWithResources(useInputStream(fis), () -> fis.close());
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param action The action to execute. This object <em>will</em> always be invoked.
|
||||||
|
* @param resources The resource actions to execute. <em>All</em> resource actions will be invoked, in the given
|
||||||
|
* order. A resource action is an instance of {@link FailableRunnable}, which will be executed.
|
||||||
|
* @see #tryWithResources(FailableRunnable, FailableConsumer, FailableRunnable...)
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static void tryWithResources(final FailableRunnable<? extends Throwable> action,
|
||||||
|
final FailableRunnable<? extends Throwable>... resources) {
|
||||||
|
tryWithResources(action, null, resources);
|
||||||
|
}
|
||||||
|
}
|
324
after/src/main/java/org/apache/commons/lang3/JavaVersion.java
Normal file
324
after/src/main/java/org/apache/commons/lang3/JavaVersion.java
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>An enum representing all the versions of the Java specification.
|
||||||
|
* This is intended to mirror available values from the
|
||||||
|
* <em>java.specification.version</em> System property. </p>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public enum JavaVersion {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Java version reported by Android. This is not an official Java version number.
|
||||||
|
*/
|
||||||
|
JAVA_0_9(1.5f, "0.9"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.1.
|
||||||
|
*/
|
||||||
|
JAVA_1_1(1.1f, "1.1"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.2.
|
||||||
|
*/
|
||||||
|
JAVA_1_2(1.2f, "1.2"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.3.
|
||||||
|
*/
|
||||||
|
JAVA_1_3(1.3f, "1.3"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.4.
|
||||||
|
*/
|
||||||
|
JAVA_1_4(1.4f, "1.4"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.5.
|
||||||
|
*/
|
||||||
|
JAVA_1_5(1.5f, "1.5"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.6.
|
||||||
|
*/
|
||||||
|
JAVA_1_6(1.6f, "1.6"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.7.
|
||||||
|
*/
|
||||||
|
JAVA_1_7(1.7f, "1.7"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.8.
|
||||||
|
*/
|
||||||
|
JAVA_1_8(1.8f, "1.8"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.9.
|
||||||
|
*
|
||||||
|
* @deprecated As of release 3.5, replaced by {@link #JAVA_9}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
JAVA_1_9(9.0f, "9"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 9.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
JAVA_9(9.0f, "9"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 10.
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
JAVA_10(10.0f, "10"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 11.
|
||||||
|
*
|
||||||
|
* @since 3.8
|
||||||
|
*/
|
||||||
|
JAVA_11(11.0f, "11"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 12.
|
||||||
|
*
|
||||||
|
* @since 3.9
|
||||||
|
*/
|
||||||
|
JAVA_12(12.0f, "12"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 13.
|
||||||
|
*
|
||||||
|
* @since 3.9
|
||||||
|
*/
|
||||||
|
JAVA_13(13.0f, "13"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 14.
|
||||||
|
*
|
||||||
|
* @since 3.11
|
||||||
|
*/
|
||||||
|
JAVA_14(14.0f, "14"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 15.
|
||||||
|
*
|
||||||
|
* @since 3.11
|
||||||
|
*/
|
||||||
|
JAVA_15(15.0f, "15"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 16.
|
||||||
|
*
|
||||||
|
* @since 3.11
|
||||||
|
*/
|
||||||
|
JAVA_16(16.0f, "16"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 17.
|
||||||
|
*
|
||||||
|
* @since 3.12.0
|
||||||
|
*/
|
||||||
|
JAVA_17(17.0f, "17"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The most recent java version. Mainly introduced to avoid to break when a new version of Java is used.
|
||||||
|
*/
|
||||||
|
JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The float value.
|
||||||
|
*/
|
||||||
|
private final float value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard name.
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param value the float value
|
||||||
|
* @param name the standard name, not null
|
||||||
|
*/
|
||||||
|
JavaVersion(final float value, final String name) {
|
||||||
|
this.value = value;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Whether this version of Java is at least the version of Java passed in.</p>
|
||||||
|
*
|
||||||
|
* <p>For example:<br>
|
||||||
|
* {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}<p>
|
||||||
|
*
|
||||||
|
* @param requiredVersion the version to check against, not null
|
||||||
|
* @return true if this version is equal to or greater than the specified version
|
||||||
|
*/
|
||||||
|
public boolean atLeast(final JavaVersion requiredVersion) {
|
||||||
|
return this.value >= requiredVersion.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Whether this version of Java is at most the version of Java passed in.</p>
|
||||||
|
*
|
||||||
|
* <p>For example:<br>
|
||||||
|
* {@code myVersion.atMost(JavaVersion.JAVA_1_4)}<p>
|
||||||
|
*
|
||||||
|
* @param requiredVersion the version to check against, not null
|
||||||
|
* @return true if this version is equal to or greater than the specified version
|
||||||
|
* @since 3.9
|
||||||
|
*/
|
||||||
|
public boolean atMost(final JavaVersion requiredVersion) {
|
||||||
|
return this.value <= requiredVersion.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the given string with a Java version number to the
|
||||||
|
* corresponding constant of this enumeration class. This method is used
|
||||||
|
* internally.
|
||||||
|
*
|
||||||
|
* @param nom the Java version as string
|
||||||
|
* @return the corresponding enumeration constant or <b>null</b> if the
|
||||||
|
* version is unknown
|
||||||
|
*/
|
||||||
|
// helper for static importing
|
||||||
|
static JavaVersion getJavaVersion(final String nom) {
|
||||||
|
return get(nom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the given string with a Java version number to the
|
||||||
|
* corresponding constant of this enumeration class. This method is used
|
||||||
|
* internally.
|
||||||
|
*
|
||||||
|
* @param versionStr the Java version as string
|
||||||
|
* @return the corresponding enumeration constant or <b>null</b> if the
|
||||||
|
* version is unknown
|
||||||
|
*/
|
||||||
|
static JavaVersion get(final String versionStr) {
|
||||||
|
if (versionStr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (versionStr) {
|
||||||
|
case "0.9":
|
||||||
|
return JAVA_0_9;
|
||||||
|
case "1.1":
|
||||||
|
return JAVA_1_1;
|
||||||
|
case "1.2":
|
||||||
|
return JAVA_1_2;
|
||||||
|
case "1.3":
|
||||||
|
return JAVA_1_3;
|
||||||
|
case "1.4":
|
||||||
|
return JAVA_1_4;
|
||||||
|
case "1.5":
|
||||||
|
return JAVA_1_5;
|
||||||
|
case "1.6":
|
||||||
|
return JAVA_1_6;
|
||||||
|
case "1.7":
|
||||||
|
return JAVA_1_7;
|
||||||
|
case "1.8":
|
||||||
|
return JAVA_1_8;
|
||||||
|
case "9":
|
||||||
|
return JAVA_9;
|
||||||
|
case "10":
|
||||||
|
return JAVA_10;
|
||||||
|
case "11":
|
||||||
|
return JAVA_11;
|
||||||
|
case "12":
|
||||||
|
return JAVA_12;
|
||||||
|
case "13":
|
||||||
|
return JAVA_13;
|
||||||
|
case "14":
|
||||||
|
return JAVA_14;
|
||||||
|
case "15":
|
||||||
|
return JAVA_15;
|
||||||
|
case "16":
|
||||||
|
return JAVA_16;
|
||||||
|
case "17":
|
||||||
|
return JAVA_17;
|
||||||
|
default:
|
||||||
|
final float v = toFloatVersion(versionStr);
|
||||||
|
if ((v - 1.) < 1.) { // then we need to check decimals > .9
|
||||||
|
final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(','));
|
||||||
|
final int end = Math.max(versionStr.length(), versionStr.indexOf(',', firstComma));
|
||||||
|
if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) {
|
||||||
|
return JAVA_RECENT;
|
||||||
|
}
|
||||||
|
} else if (v > 10) {
|
||||||
|
return JAVA_RECENT;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>The string value is overridden to return the standard name.</p>
|
||||||
|
*
|
||||||
|
* <p>For example, {@code "1.5"}.</p>
|
||||||
|
*
|
||||||
|
* @return the name, not null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set.
|
||||||
|
*
|
||||||
|
* @return the value of {@code java.specification.version} system property or 99.0 if it is not set.
|
||||||
|
*/
|
||||||
|
private static float maxVersion() {
|
||||||
|
final float v = toFloatVersion(System.getProperty("java.specification.version", "99.0"));
|
||||||
|
if (v > 0) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return 99f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a float value from a String.
|
||||||
|
*
|
||||||
|
* @param value the String to parse.
|
||||||
|
* @return the float value represented by the string or -1 if the given String can not be parsed.
|
||||||
|
*/
|
||||||
|
private static float toFloatVersion(final String value) {
|
||||||
|
final int defaultReturnValue = -1;
|
||||||
|
if (value.contains(".")) {
|
||||||
|
final String[] toParse = value.split("\\.");
|
||||||
|
if (toParse.length >= 2) {
|
||||||
|
return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NumberUtils.toFloat(value, defaultReturnValue);
|
||||||
|
}
|
||||||
|
return defaultReturnValue;
|
||||||
|
}
|
||||||
|
}
|
361
after/src/main/java/org/apache/commons/lang3/LocaleUtils.java
Normal file
361
after/src/main/java/org/apache/commons/lang3/LocaleUtils.java
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Operations to assist when working with a {@link Locale}.</p>
|
||||||
|
*
|
||||||
|
* <p>This class tries to handle {@code null} input gracefully.
|
||||||
|
* An exception will not be thrown for a {@code null} input.
|
||||||
|
* Each method documents its behavior in more detail.</p>
|
||||||
|
*
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public class LocaleUtils {
|
||||||
|
|
||||||
|
// class to avoid synchronization (Init on demand)
|
||||||
|
static class SyncAvoid {
|
||||||
|
/** Unmodifiable list of available locales. */
|
||||||
|
private static final List<Locale> AVAILABLE_LOCALE_LIST;
|
||||||
|
/** Unmodifiable set of available locales. */
|
||||||
|
private static final Set<Locale> AVAILABLE_LOCALE_SET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
final List<Locale> list = new ArrayList<>(Arrays.asList(Locale.getAvailableLocales())); // extra safe
|
||||||
|
AVAILABLE_LOCALE_LIST = Collections.unmodifiableList(list);
|
||||||
|
AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet<>(list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Concurrent map of language locales by country. */
|
||||||
|
private static final ConcurrentMap<String, List<Locale>> cLanguagesByCountry =
|
||||||
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/** Concurrent map of country locales by language. */
|
||||||
|
private static final ConcurrentMap<String, List<Locale>> cCountriesByLanguage =
|
||||||
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains an unmodifiable list of installed locales.</p>
|
||||||
|
*
|
||||||
|
* <p>This method is a wrapper around {@link Locale#getAvailableLocales()}.
|
||||||
|
* It is more efficient, as the JDK method must create a new array each
|
||||||
|
* time it is called.</p>
|
||||||
|
*
|
||||||
|
* @return the unmodifiable list of available locales
|
||||||
|
*/
|
||||||
|
public static List<Locale> availableLocaleList() {
|
||||||
|
return SyncAvoid.AVAILABLE_LOCALE_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains an unmodifiable set of installed locales.</p>
|
||||||
|
*
|
||||||
|
* <p>This method is a wrapper around {@link Locale#getAvailableLocales()}.
|
||||||
|
* It is more efficient, as the JDK method must create a new array each
|
||||||
|
* time it is called.</p>
|
||||||
|
*
|
||||||
|
* @return the unmodifiable set of available locales
|
||||||
|
*/
|
||||||
|
public static Set<Locale> availableLocaleSet() {
|
||||||
|
return SyncAvoid.AVAILABLE_LOCALE_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the list of countries supported for a given language.</p>
|
||||||
|
*
|
||||||
|
* <p>This method takes a language code and searches to find the
|
||||||
|
* countries available for that language. Variant locales are removed.</p>
|
||||||
|
*
|
||||||
|
* @param languageCode the 2 letter language code, null returns empty
|
||||||
|
* @return an unmodifiable List of Locale objects, not null
|
||||||
|
*/
|
||||||
|
public static List<Locale> countriesByLanguage(final String languageCode) {
|
||||||
|
if (languageCode == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<Locale> countries = cCountriesByLanguage.get(languageCode);
|
||||||
|
if (countries == null) {
|
||||||
|
countries = new ArrayList<>();
|
||||||
|
final List<Locale> locales = availableLocaleList();
|
||||||
|
for (final Locale locale : locales) {
|
||||||
|
if (languageCode.equals(locale.getLanguage()) &&
|
||||||
|
!locale.getCountry().isEmpty() &&
|
||||||
|
locale.getVariant().isEmpty()) {
|
||||||
|
countries.add(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countries = Collections.unmodifiableList(countries);
|
||||||
|
cCountriesByLanguage.putIfAbsent(languageCode, countries);
|
||||||
|
countries = cCountriesByLanguage.get(languageCode);
|
||||||
|
}
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if the locale specified is in the list of available locales.</p>
|
||||||
|
*
|
||||||
|
* @param locale the Locale object to check if it is available
|
||||||
|
* @return true if the locale is a known locale
|
||||||
|
*/
|
||||||
|
public static boolean isAvailableLocale(final Locale locale) {
|
||||||
|
return availableLocaleList().contains(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given String is a ISO 3166 alpha-2 country code.
|
||||||
|
*
|
||||||
|
* @param str the String to check
|
||||||
|
* @return true, is the given String is a ISO 3166 compliant country code.
|
||||||
|
*/
|
||||||
|
private static boolean isISO3166CountryCode(final String str) {
|
||||||
|
return StringUtils.isAllUpperCase(str) && str.length() == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given String is a ISO 639 compliant language code.
|
||||||
|
*
|
||||||
|
* @param str the String to check.
|
||||||
|
* @return true, if the given String is a ISO 639 compliant language code.
|
||||||
|
*/
|
||||||
|
private static boolean isISO639LanguageCode(final String str) {
|
||||||
|
return StringUtils.isAllLowerCase(str) && (str.length() == 2 || str.length() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given String is a UN M.49 numeric area code.
|
||||||
|
*
|
||||||
|
* @param str the String to check
|
||||||
|
* @return true, is the given String is a UN M.49 numeric area code.
|
||||||
|
*/
|
||||||
|
private static boolean isNumericAreaCode(final String str) {
|
||||||
|
return StringUtils.isNumeric(str) && str.length() == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the list of languages supported for a given country.</p>
|
||||||
|
*
|
||||||
|
* <p>This method takes a country code and searches to find the
|
||||||
|
* languages available for that country. Variant locales are removed.</p>
|
||||||
|
*
|
||||||
|
* @param countryCode the 2 letter country code, null returns empty
|
||||||
|
* @return an unmodifiable List of Locale objects, not null
|
||||||
|
*/
|
||||||
|
public static List<Locale> languagesByCountry(final String countryCode) {
|
||||||
|
if (countryCode == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<Locale> langs = cLanguagesByCountry.get(countryCode);
|
||||||
|
if (langs == null) {
|
||||||
|
langs = new ArrayList<>();
|
||||||
|
final List<Locale> locales = availableLocaleList();
|
||||||
|
for (final Locale locale : locales) {
|
||||||
|
if (countryCode.equals(locale.getCountry()) &&
|
||||||
|
locale.getVariant().isEmpty()) {
|
||||||
|
langs.add(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
langs = Collections.unmodifiableList(langs);
|
||||||
|
cLanguagesByCountry.putIfAbsent(countryCode, langs);
|
||||||
|
langs = cLanguagesByCountry.get(countryCode);
|
||||||
|
}
|
||||||
|
return langs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the list of locales to search through when performing
|
||||||
|
* a locale search.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* localeLookupList(Locale("fr", "CA", "xxx"))
|
||||||
|
* = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr")]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param locale the locale to start from
|
||||||
|
* @return the unmodifiable list of Locale objects, 0 being locale, not null
|
||||||
|
*/
|
||||||
|
public static List<Locale> localeLookupList(final Locale locale) {
|
||||||
|
return localeLookupList(locale, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains the list of locales to search through when performing
|
||||||
|
* a locale search.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
|
||||||
|
* = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr"), Locale("en"]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>The result list begins with the most specific locale, then the
|
||||||
|
* next more general and so on, finishing with the default locale.
|
||||||
|
* The list will never contain the same locale twice.</p>
|
||||||
|
*
|
||||||
|
* @param locale the locale to start from, null returns empty list
|
||||||
|
* @param defaultLocale the default locale to use if no other is found
|
||||||
|
* @return the unmodifiable list of Locale objects, 0 being locale, not null
|
||||||
|
*/
|
||||||
|
public static List<Locale> localeLookupList(final Locale locale, final Locale defaultLocale) {
|
||||||
|
final List<Locale> list = new ArrayList<>(4);
|
||||||
|
if (locale != null) {
|
||||||
|
list.add(locale);
|
||||||
|
if (!locale.getVariant().isEmpty()) {
|
||||||
|
list.add(new Locale(locale.getLanguage(), locale.getCountry()));
|
||||||
|
}
|
||||||
|
if (!locale.getCountry().isEmpty()) {
|
||||||
|
list.add(new Locale(locale.getLanguage(), StringUtils.EMPTY));
|
||||||
|
}
|
||||||
|
if (!list.contains(defaultLocale)) {
|
||||||
|
list.add(defaultLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to parse a locale from the given String.
|
||||||
|
*
|
||||||
|
* @param str the String to parse a locale from.
|
||||||
|
* @return a Locale instance parsed from the given String.
|
||||||
|
* @throws IllegalArgumentException if the given String can not be parsed.
|
||||||
|
*/
|
||||||
|
private static Locale parseLocale(final String str) {
|
||||||
|
if (isISO639LanguageCode(str)) {
|
||||||
|
return new Locale(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] segments = str.split("_", -1);
|
||||||
|
final String language = segments[0];
|
||||||
|
if (segments.length == 2) {
|
||||||
|
final String country = segments[1];
|
||||||
|
if (isISO639LanguageCode(language) && isISO3166CountryCode(country) ||
|
||||||
|
isNumericAreaCode(country)) {
|
||||||
|
return new Locale(language, country);
|
||||||
|
}
|
||||||
|
} else if (segments.length == 3) {
|
||||||
|
final String country = segments[1];
|
||||||
|
final String variant = segments[2];
|
||||||
|
if (isISO639LanguageCode(language) &&
|
||||||
|
(country.isEmpty() || isISO3166CountryCode(country) || isNumericAreaCode(country)) &&
|
||||||
|
!variant.isEmpty()) {
|
||||||
|
return new Locale(language, country, variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given locale if non-{@code null}, otherwise {@link Locale#getDefault()}.
|
||||||
|
*
|
||||||
|
* @param locale a locale or {@code null}.
|
||||||
|
* @return the given locale if non-{@code null}, otherwise {@link Locale#getDefault()}.
|
||||||
|
* @since 3.12.0
|
||||||
|
*/
|
||||||
|
public static Locale toLocale(final Locale locale) {
|
||||||
|
return locale != null ? locale : Locale.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts a String to a Locale.</p>
|
||||||
|
*
|
||||||
|
* <p>This method takes the string format of a locale and creates the
|
||||||
|
* locale object from it.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* LocaleUtils.toLocale("") = new Locale("", "")
|
||||||
|
* LocaleUtils.toLocale("en") = new Locale("en", "")
|
||||||
|
* LocaleUtils.toLocale("en_GB") = new Locale("en", "GB")
|
||||||
|
* LocaleUtils.toLocale("en_001") = new Locale("en", "001")
|
||||||
|
* LocaleUtils.toLocale("en_GB_xxx") = new Locale("en", "GB", "xxx") (#)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>(#) The behavior of the JDK variant constructor changed between JDK1.3 and JDK1.4.
|
||||||
|
* In JDK1.3, the constructor upper cases the variant, in JDK1.4, it doesn't.
|
||||||
|
* Thus, the result from getVariant() may vary depending on your JDK.</p>
|
||||||
|
*
|
||||||
|
* <p>This method validates the input strictly.
|
||||||
|
* The language code must be lowercase.
|
||||||
|
* The country code must be uppercase.
|
||||||
|
* The separator must be an underscore.
|
||||||
|
* The length must be correct.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param str the locale String to convert, null returns null
|
||||||
|
* @return a Locale, null if null input
|
||||||
|
* @throws IllegalArgumentException if the string is an invalid format
|
||||||
|
* @see Locale#forLanguageTag(String)
|
||||||
|
*/
|
||||||
|
public static Locale toLocale(final String str) {
|
||||||
|
if (str == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (str.isEmpty()) { // LANG-941 - JDK 8 introduced an empty locale where all fields are blank
|
||||||
|
return new Locale(StringUtils.EMPTY, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
if (str.contains("#")) { // LANG-879 - Cannot handle Java 7 script & extensions
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
final int len = str.length();
|
||||||
|
if (len < 2) {
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
final char ch0 = str.charAt(0);
|
||||||
|
if (ch0 == '_') {
|
||||||
|
if (len < 3) {
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
final char ch1 = str.charAt(1);
|
||||||
|
final char ch2 = str.charAt(2);
|
||||||
|
if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) {
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
if (len == 3) {
|
||||||
|
return new Locale(StringUtils.EMPTY, str.substring(1, 3));
|
||||||
|
}
|
||||||
|
if (len < 5) {
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
if (str.charAt(3) != '_') {
|
||||||
|
throw new IllegalArgumentException("Invalid locale format: " + str);
|
||||||
|
}
|
||||||
|
return new Locale(StringUtils.EMPTY, str.substring(1, 3), str.substring(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseLocale(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code LocaleUtils} instances should NOT be constructed in standard programming.
|
||||||
|
* Instead, the class should be used as {@code LocaleUtils.toLocale("en_GB");}.</p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||||
|
* to operate.</p>
|
||||||
|
*/
|
||||||
|
public LocaleUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Thrown to indicate that a block of code has not been implemented.
|
||||||
|
* This exception supplements {@code UnsupportedOperationException}
|
||||||
|
* by providing a more semantically rich description of the problem.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code NotImplementedException} represents the case where the
|
||||||
|
* author has yet to implement the logic at this point in the program.
|
||||||
|
* This can act as an exception based TODO tag. </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public void foo() {
|
||||||
|
* try {
|
||||||
|
* // do something that throws an Exception
|
||||||
|
* } catch (Exception ex) {
|
||||||
|
* // don't know what to do here yet
|
||||||
|
* throw new NotImplementedException("TODO", ex);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* This class was originally added in Lang 2.0, but removed in 3.0.
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public class NotImplementedException extends UnsupportedOperationException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 20131021L;
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public NotImplementedException() {
|
||||||
|
this.code = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @param message description of the exception
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public NotImplementedException(final String message) {
|
||||||
|
this(message, (String) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @param cause cause of the exception
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public NotImplementedException(final Throwable cause) {
|
||||||
|
this(cause, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @param message description of the exception
|
||||||
|
* @param cause cause of the exception
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public NotImplementedException(final String message, final Throwable cause) {
|
||||||
|
this(message, cause, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @param message description of the exception
|
||||||
|
* @param code code indicating a resource for more information regarding the lack of implementation
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public NotImplementedException(final String message, final String code) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @param cause cause of the exception
|
||||||
|
* @param code code indicating a resource for more information regarding the lack of implementation
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public NotImplementedException(final Throwable cause, final String code) {
|
||||||
|
super(cause);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NotImplementedException.
|
||||||
|
*
|
||||||
|
* @param message description of the exception
|
||||||
|
* @param cause cause of the exception
|
||||||
|
* @param code code indicating a resource for more information regarding the lack of implementation
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public NotImplementedException(final String message, final Throwable cause, final String code) {
|
||||||
|
super(message, cause);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the not implemented code. This is an unformatted piece of text intended to point to
|
||||||
|
* further information regarding the lack of implementation. It might, for example, be an issue
|
||||||
|
* tracker ID or a URL.
|
||||||
|
*
|
||||||
|
* @return a code indicating a resource for more information regarding the lack of implementation
|
||||||
|
*/
|
||||||
|
public String getCode() {
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
}
|
1350
after/src/main/java/org/apache/commons/lang3/ObjectUtils.java
Normal file
1350
after/src/main/java/org/apache/commons/lang3/ObjectUtils.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,477 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Generates random {@code String}s.</p>
|
||||||
|
*
|
||||||
|
* <p><b>Caveat: Instances of {@link Random}, upon which the implementation of this
|
||||||
|
* class relies, are not cryptographically secure.</b></p>
|
||||||
|
*
|
||||||
|
* <p>RandomStringUtils is intended for simple use cases. For more advanced
|
||||||
|
* use cases consider using Apache Commons Text's
|
||||||
|
* <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/RandomStringGenerator.html">
|
||||||
|
* RandomStringGenerator</a> instead.</p>
|
||||||
|
*
|
||||||
|
* <p>The Apache Commons project provides
|
||||||
|
* <a href="https://commons.apache.org/rng">Commons RNG</a> dedicated to pseudo-random number generation, that may be
|
||||||
|
* a better choice for applications with more stringent requirements
|
||||||
|
* (performance and/or correctness).</p>
|
||||||
|
*
|
||||||
|
* <p>Note that <em>private high surrogate</em> characters are ignored.
|
||||||
|
* These are Unicode characters that fall between the values 56192 (db80)
|
||||||
|
* and 56319 (dbff) as we don't know how to handle them.
|
||||||
|
* High and low surrogates are correctly dealt with - that is if a
|
||||||
|
* high surrogate is randomly chosen, 55296 (d800) to 56191 (db7f)
|
||||||
|
* then it is followed by a low surrogate. If a low surrogate is chosen,
|
||||||
|
* 56320 (dc00) to 57343 (dfff) then it is placed after a randomly
|
||||||
|
* chosen high surrogate.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class RandomStringUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Random object used by random method. This has to be not local
|
||||||
|
* to the random method so as to not return the same value in the
|
||||||
|
* same millisecond.</p>
|
||||||
|
*/
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code RandomStringUtils} instances should NOT be constructed in
|
||||||
|
* standard programming. Instead, the class should be used as
|
||||||
|
* {@code RandomStringUtils.random(5);}.</p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||||
|
* to operate.</p>
|
||||||
|
*/
|
||||||
|
public RandomStringUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of all characters.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String random(final int count) {
|
||||||
|
return random(count, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of characters whose
|
||||||
|
* ASCII value is between {@code 32} and {@code 126} (inclusive).</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String randomAscii(final int count) {
|
||||||
|
return random(count, 32, 127, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||||
|
* the exclusive maximum.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of characters whose
|
||||||
|
* ASCII value is between {@code 32} and {@code 126} (inclusive).</p>
|
||||||
|
*
|
||||||
|
* @param minLengthInclusive the inclusive minimum length of the string to generate
|
||||||
|
* @param maxLengthExclusive the exclusive maximum length of the string to generate
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomAscii(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||||
|
return randomAscii(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of Latin alphabetic
|
||||||
|
* characters (a-z, A-Z).</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String randomAlphabetic(final int count) {
|
||||||
|
return random(count, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||||
|
* the exclusive maximum.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z).</p>
|
||||||
|
*
|
||||||
|
* @param minLengthInclusive the inclusive minimum length of the string to generate
|
||||||
|
* @param maxLengthExclusive the exclusive maximum length of the string to generate
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomAlphabetic(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||||
|
return randomAlphabetic(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of Latin alphabetic
|
||||||
|
* characters (a-z, A-Z) and the digits 0-9.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String randomAlphanumeric(final int count) {
|
||||||
|
return random(count, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||||
|
* the exclusive maximum.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of Latin alphabetic
|
||||||
|
* characters (a-z, A-Z) and the digits 0-9.</p>
|
||||||
|
*
|
||||||
|
* @param minLengthInclusive the inclusive minimum length of the string to generate
|
||||||
|
* @param maxLengthExclusive the exclusive maximum length of the string to generate
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||||
|
return randomAlphanumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of characters which match the POSIX [:graph:]
|
||||||
|
* regular expression character class. This class contains all visible ASCII characters
|
||||||
|
* (i.e. anything except spaces and control characters).</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomGraph(final int count) {
|
||||||
|
return random(count, 33, 126, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||||
|
* the exclusive maximum.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of \p{Graph} characters.</p>
|
||||||
|
*
|
||||||
|
* @param minLengthInclusive the inclusive minimum length of the string to generate
|
||||||
|
* @param maxLengthExclusive the exclusive maximum length of the string to generate
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomGraph(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||||
|
return randomGraph(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of numeric
|
||||||
|
* characters.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String randomNumeric(final int count) {
|
||||||
|
return random(count, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||||
|
* the exclusive maximum.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of \p{Digit} characters.</p>
|
||||||
|
*
|
||||||
|
* @param minLengthInclusive the inclusive minimum length of the string to generate
|
||||||
|
* @param maxLengthExclusive the exclusive maximum length of the string to generate
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomNumeric(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||||
|
return randomNumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of characters which match the POSIX [:print:]
|
||||||
|
* regular expression character class. This class includes all visible ASCII characters and spaces
|
||||||
|
* (i.e. anything except control characters).</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomPrint(final int count) {
|
||||||
|
return random(count, 32, 126, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is between the inclusive minimum and
|
||||||
|
* the exclusive maximum.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of \p{Print} characters.</p>
|
||||||
|
*
|
||||||
|
* @param minLengthInclusive the inclusive minimum length of the string to generate
|
||||||
|
* @param maxLengthExclusive the exclusive maximum length of the string to generate
|
||||||
|
* @return the random string
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static String randomPrint(final int minLengthInclusive, final int maxLengthExclusive) {
|
||||||
|
return randomPrint(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of alpha-numeric
|
||||||
|
* characters as indicated by the arguments.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @param letters if {@code true}, generated string may include
|
||||||
|
* alphabetic characters
|
||||||
|
* @param numbers if {@code true}, generated string may include
|
||||||
|
* numeric characters
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String random(final int count, final boolean letters, final boolean numbers) {
|
||||||
|
return random(count, 0, 0, letters, numbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of alpha-numeric
|
||||||
|
* characters as indicated by the arguments.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @param start the position in set of chars to start at
|
||||||
|
* @param end the position in set of chars to end before
|
||||||
|
* @param letters if {@code true}, generated string may include
|
||||||
|
* alphabetic characters
|
||||||
|
* @param numbers if {@code true}, generated string may include
|
||||||
|
* numeric characters
|
||||||
|
* @return the random string
|
||||||
|
*/
|
||||||
|
public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers) {
|
||||||
|
return random(count, start, end, letters, numbers, null, RANDOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string based on a variety of options, using
|
||||||
|
* default source of randomness.</p>
|
||||||
|
*
|
||||||
|
* <p>This method has exactly the same semantics as
|
||||||
|
* {@link #random(int,int,int,boolean,boolean,char[],Random)}, but
|
||||||
|
* instead of using an externally supplied source of randomness, it uses
|
||||||
|
* the internal static {@link Random} instance.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @param start the position in set of chars to start at
|
||||||
|
* @param end the position in set of chars to end before
|
||||||
|
* @param letters if {@code true}, generated string may include
|
||||||
|
* alphabetic characters
|
||||||
|
* @param numbers if {@code true}, generated string may include
|
||||||
|
* numeric characters
|
||||||
|
* @param chars the set of chars to choose randoms from.
|
||||||
|
* If {@code null}, then it will use the set of all chars.
|
||||||
|
* @return the random string
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if there are not
|
||||||
|
* {@code (end - start) + 1} characters in the set array.
|
||||||
|
*/
|
||||||
|
public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers, final char... chars) {
|
||||||
|
return random(count, start, end, letters, numbers, chars, RANDOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string based on a variety of options, using
|
||||||
|
* supplied source of randomness.</p>
|
||||||
|
*
|
||||||
|
* <p>If start and end are both {@code 0}, start and end are set
|
||||||
|
* to {@code ' '} and {@code 'z'}, the ASCII printable
|
||||||
|
* characters, will be used, unless letters and numbers are both
|
||||||
|
* {@code false}, in which case, start and end are set to
|
||||||
|
* {@code 0} and {@link Character#MAX_CODE_POINT}.
|
||||||
|
*
|
||||||
|
* <p>If set is not {@code null}, characters between start and
|
||||||
|
* end are chosen.</p>
|
||||||
|
*
|
||||||
|
* <p>This method accepts a user-supplied {@link Random}
|
||||||
|
* instance to use as a source of randomness. By seeding a single
|
||||||
|
* {@link Random} instance with a fixed seed and using it for each call,
|
||||||
|
* the same random sequence of strings can be generated repeatedly
|
||||||
|
* and predictably.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @param start the position in set of chars to start at (inclusive)
|
||||||
|
* @param end the position in set of chars to end before (exclusive)
|
||||||
|
* @param letters if {@code true}, generated string may include
|
||||||
|
* alphabetic characters
|
||||||
|
* @param numbers if {@code true}, generated string may include
|
||||||
|
* numeric characters
|
||||||
|
* @param chars the set of chars to choose randoms from, must not be empty.
|
||||||
|
* If {@code null}, then it will use the set of all chars.
|
||||||
|
* @param random a source of randomness.
|
||||||
|
* @return the random string
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if there are not
|
||||||
|
* {@code (end - start) + 1} characters in the set array.
|
||||||
|
* @throws IllegalArgumentException if {@code count} < 0 or the provided chars array is empty.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static String random(int count, int start, int end, final boolean letters, final boolean numbers,
|
||||||
|
final char[] chars, final Random random) {
|
||||||
|
if (count == 0) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
} else if (count < 0) {
|
||||||
|
throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
|
||||||
|
}
|
||||||
|
if (chars != null && chars.length == 0) {
|
||||||
|
throw new IllegalArgumentException("The chars array must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == 0 && end == 0) {
|
||||||
|
if (chars != null) {
|
||||||
|
end = chars.length;
|
||||||
|
} else if (!letters && !numbers) {
|
||||||
|
end = Character.MAX_CODE_POINT;
|
||||||
|
} else {
|
||||||
|
end = 'z' + 1;
|
||||||
|
start = ' ';
|
||||||
|
}
|
||||||
|
} else if (end <= start) {
|
||||||
|
throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int zero_digit_ascii = 48;
|
||||||
|
final int first_letter_ascii = 65;
|
||||||
|
|
||||||
|
if (chars == null && (numbers && end <= zero_digit_ascii
|
||||||
|
|| letters && end <= first_letter_ascii)) {
|
||||||
|
throw new IllegalArgumentException("Parameter end (" + end + ") must be greater then (" + zero_digit_ascii + ") for generating digits " +
|
||||||
|
"or greater then (" + first_letter_ascii + ") for generating letters.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(count);
|
||||||
|
final int gap = end - start;
|
||||||
|
|
||||||
|
while (count-- != 0) {
|
||||||
|
final int codePoint;
|
||||||
|
if (chars == null) {
|
||||||
|
codePoint = random.nextInt(gap) + start;
|
||||||
|
|
||||||
|
switch (Character.getType(codePoint)) {
|
||||||
|
case Character.UNASSIGNED:
|
||||||
|
case Character.PRIVATE_USE:
|
||||||
|
case Character.SURROGATE:
|
||||||
|
count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
codePoint = chars[random.nextInt(gap) + start];
|
||||||
|
}
|
||||||
|
|
||||||
|
final int numberOfChars = Character.charCount(codePoint);
|
||||||
|
if (count == 0 && numberOfChars > 1) {
|
||||||
|
count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (letters && Character.isLetter(codePoint)
|
||||||
|
|| numbers && Character.isDigit(codePoint)
|
||||||
|
|| !letters && !numbers) {
|
||||||
|
builder.appendCodePoint(codePoint);
|
||||||
|
|
||||||
|
if (numberOfChars == 2) {
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of characters
|
||||||
|
* specified by the string, must not be empty.
|
||||||
|
* If null, the set of all characters is used.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @param chars the String containing the set of characters to use,
|
||||||
|
* may be null, but must not be empty
|
||||||
|
* @return the random string
|
||||||
|
* @throws IllegalArgumentException if {@code count} < 0 or the string is empty.
|
||||||
|
*/
|
||||||
|
public static String random(final int count, final String chars) {
|
||||||
|
if (chars == null) {
|
||||||
|
return random(count, 0, 0, false, false, null, RANDOM);
|
||||||
|
}
|
||||||
|
return random(count, chars.toCharArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a random string whose length is the number of characters
|
||||||
|
* specified.</p>
|
||||||
|
*
|
||||||
|
* <p>Characters will be chosen from the set of characters specified.</p>
|
||||||
|
*
|
||||||
|
* @param count the length of random string to create
|
||||||
|
* @param chars the character array containing the set of characters to use,
|
||||||
|
* may be null
|
||||||
|
* @return the random string
|
||||||
|
* @throws IllegalArgumentException if {@code count} < 0.
|
||||||
|
*/
|
||||||
|
public static String random(final int count, final char... chars) {
|
||||||
|
if (chars == null) {
|
||||||
|
return random(count, 0, 0, false, false, null, RANDOM);
|
||||||
|
}
|
||||||
|
return random(count, 0, chars.length, false, false, chars, RANDOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
254
after/src/main/java/org/apache/commons/lang3/RandomUtils.java
Normal file
254
after/src/main/java/org/apache/commons/lang3/RandomUtils.java
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Utility library that supplements the standard {@link Random} class.</p>
|
||||||
|
*
|
||||||
|
* <p>Caveat: Instances of {@link Random} are not cryptographically secure.</p>
|
||||||
|
*
|
||||||
|
* <p>Please note that the Apache Commons project provides a component
|
||||||
|
* dedicated to pseudo-random number generation, namely
|
||||||
|
* <a href="https://commons.apache.org/rng">Commons RNG</a>, that may be
|
||||||
|
* a better choice for applications with more stringent requirements
|
||||||
|
* (performance and/or correctness).</p>
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class RandomUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random object used by random method. This has to be not local to the
|
||||||
|
* random method so as to not return the same value in the same millisecond.
|
||||||
|
*/
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* {@code RandomUtils} instances should NOT be constructed in standard
|
||||||
|
* programming. Instead, the class should be used as
|
||||||
|
* {@code RandomUtils.nextBytes(5);}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor is public to permit tools that require a JavaBean
|
||||||
|
* instance to operate.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public RandomUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns a random boolean value
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the random boolean
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static boolean nextBoolean() {
|
||||||
|
return RANDOM.nextBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Creates an array of random bytes.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param count
|
||||||
|
* the size of the returned array
|
||||||
|
* @return the random byte array
|
||||||
|
* @throws IllegalArgumentException if {@code count} is negative
|
||||||
|
*/
|
||||||
|
public static byte[] nextBytes(final int count) {
|
||||||
|
Validate.isTrue(count >= 0, "Count cannot be negative.");
|
||||||
|
|
||||||
|
final byte[] result = new byte[count];
|
||||||
|
RANDOM.nextBytes(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns a random integer within the specified range.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param startInclusive
|
||||||
|
* the smallest value that can be returned, must be non-negative
|
||||||
|
* @param endExclusive
|
||||||
|
* the upper bound (not included)
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code startInclusive > endExclusive} or if
|
||||||
|
* {@code startInclusive} is negative
|
||||||
|
* @return the random integer
|
||||||
|
*/
|
||||||
|
public static int nextInt(final int startInclusive, final int endExclusive) {
|
||||||
|
Validate.isTrue(endExclusive >= startInclusive,
|
||||||
|
"Start value must be smaller or equal to end value.");
|
||||||
|
Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative.");
|
||||||
|
|
||||||
|
if (startInclusive == endExclusive) {
|
||||||
|
return startInclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return startInclusive + RANDOM.nextInt(endExclusive - startInclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> Returns a random int within 0 - Integer.MAX_VALUE </p>
|
||||||
|
*
|
||||||
|
* @return the random integer
|
||||||
|
* @see #nextInt(int, int)
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static int nextInt() {
|
||||||
|
return nextInt(0, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns a random long within the specified range.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param startInclusive
|
||||||
|
* the smallest value that can be returned, must be non-negative
|
||||||
|
* @param endExclusive
|
||||||
|
* the upper bound (not included)
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code startInclusive > endExclusive} or if
|
||||||
|
* {@code startInclusive} is negative
|
||||||
|
* @return the random long
|
||||||
|
*/
|
||||||
|
public static long nextLong(final long startInclusive, final long endExclusive) {
|
||||||
|
Validate.isTrue(endExclusive >= startInclusive,
|
||||||
|
"Start value must be smaller or equal to end value.");
|
||||||
|
Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative.");
|
||||||
|
|
||||||
|
if (startInclusive == endExclusive) {
|
||||||
|
return startInclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return startInclusive + nextLong(endExclusive - startInclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> Returns a random long within 0 - Long.MAX_VALUE </p>
|
||||||
|
*
|
||||||
|
* @return the random long
|
||||||
|
* @see #nextLong(long, long)
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static long nextLong() {
|
||||||
|
return nextLong(Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a {@code long} value between 0 (inclusive) and the specified
|
||||||
|
* value (exclusive).
|
||||||
|
*
|
||||||
|
* @param n Bound on the random number to be returned. Must be positive.
|
||||||
|
* @return a random {@code long} value between 0 (inclusive) and {@code n}
|
||||||
|
* (exclusive).
|
||||||
|
*/
|
||||||
|
private static long nextLong(final long n) {
|
||||||
|
// Extracted from o.a.c.rng.core.BaseProvider.nextLong(long)
|
||||||
|
long bits;
|
||||||
|
long val;
|
||||||
|
do {
|
||||||
|
bits = RANDOM.nextLong() >>> 1;
|
||||||
|
val = bits % n;
|
||||||
|
} while (bits - val + (n - 1) < 0);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns a random double within the specified range.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param startInclusive
|
||||||
|
* the smallest value that can be returned, must be non-negative
|
||||||
|
* @param endExclusive
|
||||||
|
* the upper bound (not included)
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code startInclusive > endExclusive} or if
|
||||||
|
* {@code startInclusive} is negative
|
||||||
|
* @return the random double
|
||||||
|
*/
|
||||||
|
public static double nextDouble(final double startInclusive, final double endExclusive) {
|
||||||
|
Validate.isTrue(endExclusive >= startInclusive,
|
||||||
|
"Start value must be smaller or equal to end value.");
|
||||||
|
Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative.");
|
||||||
|
|
||||||
|
if (startInclusive == endExclusive) {
|
||||||
|
return startInclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> Returns a random double within 0 - Double.MAX_VALUE </p>
|
||||||
|
*
|
||||||
|
* @return the random double
|
||||||
|
* @see #nextDouble(double, double)
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static double nextDouble() {
|
||||||
|
return nextDouble(0, Double.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns a random float within the specified range.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param startInclusive
|
||||||
|
* the smallest value that can be returned, must be non-negative
|
||||||
|
* @param endExclusive
|
||||||
|
* the upper bound (not included)
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code startInclusive > endExclusive} or if
|
||||||
|
* {@code startInclusive} is negative
|
||||||
|
* @return the random float
|
||||||
|
*/
|
||||||
|
public static float nextFloat(final float startInclusive, final float endExclusive) {
|
||||||
|
Validate.isTrue(endExclusive >= startInclusive,
|
||||||
|
"Start value must be smaller or equal to end value.");
|
||||||
|
Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative.");
|
||||||
|
|
||||||
|
if (startInclusive == endExclusive) {
|
||||||
|
return startInclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextFloat());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> Returns a random float within 0 - Float.MAX_VALUE </p>
|
||||||
|
*
|
||||||
|
* @return the random float
|
||||||
|
* @see #nextFloat(float, float)
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static float nextFloat() {
|
||||||
|
return nextFloat(0, Float.MAX_VALUE);
|
||||||
|
}
|
||||||
|
}
|
512
after/src/main/java/org/apache/commons/lang3/Range.java
Normal file
512
after/src/main/java/org/apache/commons/lang3/Range.java
Normal file
|
@ -0,0 +1,512 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>An immutable range of objects from a minimum to maximum point inclusive.</p>
|
||||||
|
*
|
||||||
|
* <p>The objects need to either be implementations of {@code Comparable}
|
||||||
|
* or you need to supply a {@code Comparator}. </p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe# if the objects and comparator are thread-safe</p>
|
||||||
|
*
|
||||||
|
* @param <T> The type of range values.
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public final class Range<T> implements Serializable {
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
private enum ComparableComparator implements Comparator {
|
||||||
|
INSTANCE;
|
||||||
|
/**
|
||||||
|
* Comparable based compare implementation.
|
||||||
|
*
|
||||||
|
* @param obj1 left hand side of comparison
|
||||||
|
* @param obj2 right hand side of comparison
|
||||||
|
* @return negative, 0, positive comparison value
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compare(final Object obj1, final Object obj2) {
|
||||||
|
return ((Comparable) obj1).compareTo(obj2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialization version.
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* <p>Obtains a range with the specified minimum and maximum values (both inclusive).</p>
|
||||||
|
*
|
||||||
|
* <p>The range uses the natural ordering of the elements to determine where
|
||||||
|
* values lie in the range.</p>
|
||||||
|
*
|
||||||
|
* <p>The arguments may be passed in the order (min,max) or (max,min).
|
||||||
|
* The getMinimum and getMaximum methods will return the correct values.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the elements in this range
|
||||||
|
* @param fromInclusive the first value that defines the edge of the range, inclusive
|
||||||
|
* @param toInclusive the second value that defines the edge of the range, inclusive
|
||||||
|
* @return the range object, not null
|
||||||
|
* @throws IllegalArgumentException if either element is null
|
||||||
|
* @throws ClassCastException if the elements are not {@code Comparable}
|
||||||
|
*/
|
||||||
|
public static <T extends Comparable<T>> Range<T> between(final T fromInclusive, final T toInclusive) {
|
||||||
|
return between(fromInclusive, toInclusive, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains a range with the specified minimum and maximum values (both inclusive).</p>
|
||||||
|
*
|
||||||
|
* <p>The range uses the specified {@code Comparator} to determine where
|
||||||
|
* values lie in the range.</p>
|
||||||
|
*
|
||||||
|
* <p>The arguments may be passed in the order (min,max) or (max,min).
|
||||||
|
* The getMinimum and getMaximum methods will return the correct values.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the elements in this range
|
||||||
|
* @param fromInclusive the first value that defines the edge of the range, inclusive
|
||||||
|
* @param toInclusive the second value that defines the edge of the range, inclusive
|
||||||
|
* @param comparator the comparator to be used, null for natural ordering
|
||||||
|
* @return the range object, not null
|
||||||
|
* @throws IllegalArgumentException if either element is null
|
||||||
|
* @throws ClassCastException if using natural ordering and the elements are not {@code Comparable}
|
||||||
|
*/
|
||||||
|
public static <T> Range<T> between(final T fromInclusive, final T toInclusive, final Comparator<T> comparator) {
|
||||||
|
return new Range<>(fromInclusive, toInclusive, comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains a range using the specified element as both the minimum
|
||||||
|
* and maximum in this range.</p>
|
||||||
|
*
|
||||||
|
* <p>The range uses the natural ordering of the elements to determine where
|
||||||
|
* values lie in the range.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the elements in this range
|
||||||
|
* @param element the value to use for this range, not null
|
||||||
|
* @return the range object, not null
|
||||||
|
* @throws IllegalArgumentException if the element is null
|
||||||
|
* @throws ClassCastException if the element is not {@code Comparable}
|
||||||
|
*/
|
||||||
|
public static <T extends Comparable<T>> Range<T> is(final T element) {
|
||||||
|
return between(element, element, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Obtains a range using the specified element as both the minimum
|
||||||
|
* and maximum in this range.</p>
|
||||||
|
*
|
||||||
|
* <p>The range uses the specified {@code Comparator} to determine where
|
||||||
|
* values lie in the range.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the elements in this range
|
||||||
|
* @param element the value to use for this range, must not be {@code null}
|
||||||
|
* @param comparator the comparator to be used, null for natural ordering
|
||||||
|
* @return the range object, not null
|
||||||
|
* @throws IllegalArgumentException if the element is null
|
||||||
|
* @throws ClassCastException if using natural ordering and the elements are not {@code Comparable}
|
||||||
|
*/
|
||||||
|
public static <T> Range<T> is(final T element, final Comparator<T> comparator) {
|
||||||
|
return between(element, element, comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ordering scheme used in this range.
|
||||||
|
*/
|
||||||
|
private final Comparator<T> comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached output hashCode (class is immutable).
|
||||||
|
*/
|
||||||
|
private transient int hashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum value in this range (inclusive).
|
||||||
|
*/
|
||||||
|
private final T maximum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum value in this range (inclusive).
|
||||||
|
*/
|
||||||
|
private final T minimum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached output toString (class is immutable).
|
||||||
|
*/
|
||||||
|
private transient String toString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* @param element1 the first element, not null
|
||||||
|
* @param element2 the second element, not null
|
||||||
|
* @param comp the comparator to be used, null for natural ordering
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Range(final T element1, final T element2, final Comparator<T> comp) {
|
||||||
|
if (element1 == null || element2 == null) {
|
||||||
|
throw new IllegalArgumentException("Elements in a range must not be null: element1=" +
|
||||||
|
element1 + ", element2=" + element2);
|
||||||
|
}
|
||||||
|
if (comp == null) {
|
||||||
|
this.comparator = ComparableComparator.INSTANCE;
|
||||||
|
} else {
|
||||||
|
this.comparator = comp;
|
||||||
|
}
|
||||||
|
if (this.comparator.compare(element1, element2) < 1) {
|
||||||
|
this.minimum = element1;
|
||||||
|
this.maximum = element2;
|
||||||
|
} else {
|
||||||
|
this.minimum = element2;
|
||||||
|
this.maximum = element1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether the specified element occurs within this range.</p>
|
||||||
|
*
|
||||||
|
* @param element the element to check for, null returns false
|
||||||
|
* @return true if the specified element occurs within this range
|
||||||
|
*/
|
||||||
|
public boolean contains(final T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return comparator.compare(element, minimum) > -1 && comparator.compare(element, maximum) < 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range contains all the elements of the specified range.</p>
|
||||||
|
*
|
||||||
|
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||||
|
*
|
||||||
|
* @param otherRange the range to check, null returns false
|
||||||
|
* @return true if this range contains the specified range
|
||||||
|
* @throws RuntimeException if ranges cannot be compared
|
||||||
|
*/
|
||||||
|
public boolean containsRange(final Range<T> otherRange) {
|
||||||
|
if (otherRange == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return contains(otherRange.minimum)
|
||||||
|
&& contains(otherRange.maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks where the specified element occurs relative to this range.</p>
|
||||||
|
*
|
||||||
|
* <p>The API is reminiscent of the Comparable interface returning {@code -1} if
|
||||||
|
* the element is before the range, {@code 0} if contained within the range and
|
||||||
|
* {@code 1} if the element is after the range. </p>
|
||||||
|
*
|
||||||
|
* @param element the element to check for, not null
|
||||||
|
* @return -1, 0 or +1 depending on the element's location relative to the range
|
||||||
|
*/
|
||||||
|
public int elementCompareTo(final T element) {
|
||||||
|
// Comparable API says throw NPE on null
|
||||||
|
Validate.notNull(element, "element");
|
||||||
|
if (isAfter(element)) {
|
||||||
|
return -1;
|
||||||
|
} else if (isBefore(element)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element tests
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Compares this range to another object to test if they are equal.</p>.
|
||||||
|
*
|
||||||
|
* <p>To be equal, the minimum and maximum values must be equal, which
|
||||||
|
* ignores any differences in the comparator.</p>
|
||||||
|
*
|
||||||
|
* @param obj the reference object with which to compare
|
||||||
|
* @return true if this object is equal
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
} else if (obj == null || obj.getClass() != getClass()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
@SuppressWarnings("unchecked") // OK because we checked the class above
|
||||||
|
final
|
||||||
|
Range<T> range = (Range<T>) obj;
|
||||||
|
return minimum.equals(range.minimum) &&
|
||||||
|
maximum.equals(range.maximum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the comparator being used to determine if objects are within the range.</p>
|
||||||
|
*
|
||||||
|
* <p>Natural ordering uses an internal comparator implementation, thus this
|
||||||
|
* method never returns null. See {@link #isNaturalOrdering()}.</p>
|
||||||
|
*
|
||||||
|
* @return the comparator being used, not null
|
||||||
|
*/
|
||||||
|
public Comparator<T> getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the maximum value in this range.</p>
|
||||||
|
*
|
||||||
|
* @return the maximum value in this range, not null
|
||||||
|
*/
|
||||||
|
public T getMaximum() {
|
||||||
|
return maximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the minimum value in this range.</p>
|
||||||
|
*
|
||||||
|
* @return the minimum value in this range, not null
|
||||||
|
*/
|
||||||
|
public T getMinimum() {
|
||||||
|
return minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets a suitable hash code for the range.</p>
|
||||||
|
*
|
||||||
|
* @return a hash code value for this object
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = hashCode;
|
||||||
|
if (hashCode == 0) {
|
||||||
|
result = 17;
|
||||||
|
result = 37 * result + getClass().hashCode();
|
||||||
|
result = 37 * result + minimum.hashCode();
|
||||||
|
result = 37 * result + maximum.hashCode();
|
||||||
|
hashCode = result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the intersection of {@code this} and an overlapping Range.
|
||||||
|
* @param other overlapping Range
|
||||||
|
* @return range representing the intersection of {@code this} and {@code other} ({@code this} if equal)
|
||||||
|
* @throws IllegalArgumentException if {@code other} does not overlap {@code this}
|
||||||
|
* @since 3.0.1
|
||||||
|
*/
|
||||||
|
public Range<T> intersectionWith(final Range<T> other) {
|
||||||
|
if (!this.isOverlappedBy(other)) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Cannot calculate intersection with non-overlapping range %s", other));
|
||||||
|
}
|
||||||
|
if (this.equals(other)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
final T min = getComparator().compare(minimum, other.minimum) < 0 ? other.minimum : minimum;
|
||||||
|
final T max = getComparator().compare(maximum, other.maximum) < 0 ? maximum : other.maximum;
|
||||||
|
return between(min, max, getComparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range is after the specified element.</p>
|
||||||
|
*
|
||||||
|
* @param element the element to check for, null returns false
|
||||||
|
* @return true if this range is entirely after the specified element
|
||||||
|
*/
|
||||||
|
public boolean isAfter(final T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return comparator.compare(element, minimum) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range is completely after the specified range.</p>
|
||||||
|
*
|
||||||
|
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||||
|
*
|
||||||
|
* @param otherRange the range to check, null returns false
|
||||||
|
* @return true if this range is completely after the specified range
|
||||||
|
* @throws RuntimeException if ranges cannot be compared
|
||||||
|
*/
|
||||||
|
public boolean isAfterRange(final Range<T> otherRange) {
|
||||||
|
if (otherRange == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isAfter(otherRange.maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range is before the specified element.</p>
|
||||||
|
*
|
||||||
|
* @param element the element to check for, null returns false
|
||||||
|
* @return true if this range is entirely before the specified element
|
||||||
|
*/
|
||||||
|
public boolean isBefore(final T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return comparator.compare(element, maximum) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range is completely before the specified range.</p>
|
||||||
|
*
|
||||||
|
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||||
|
*
|
||||||
|
* @param otherRange the range to check, null returns false
|
||||||
|
* @return true if this range is completely before the specified range
|
||||||
|
* @throws RuntimeException if ranges cannot be compared
|
||||||
|
*/
|
||||||
|
public boolean isBeforeRange(final Range<T> otherRange) {
|
||||||
|
if (otherRange == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isBefore(otherRange.minimum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range ends with the specified element.</p>
|
||||||
|
*
|
||||||
|
* @param element the element to check for, null returns false
|
||||||
|
* @return true if the specified element occurs within this range
|
||||||
|
*/
|
||||||
|
public boolean isEndedBy(final T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return comparator.compare(element, maximum) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Whether or not the Range is using the natural ordering of the elements.</p>
|
||||||
|
*
|
||||||
|
* <p>Natural ordering uses an internal comparator implementation, thus this
|
||||||
|
* method is the only way to check if a null comparator was specified.</p>
|
||||||
|
*
|
||||||
|
* @return true if using natural ordering
|
||||||
|
*/
|
||||||
|
public boolean isNaturalOrdering() {
|
||||||
|
return comparator == ComparableComparator.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range is overlapped by the specified range.</p>
|
||||||
|
*
|
||||||
|
* <p>Two ranges overlap if there is at least one element in common.</p>
|
||||||
|
*
|
||||||
|
* <p>This method may fail if the ranges have two different comparators or element types.</p>
|
||||||
|
*
|
||||||
|
* @param otherRange the range to test, null returns false
|
||||||
|
* @return true if the specified range overlaps with this
|
||||||
|
* range; otherwise, {@code false}
|
||||||
|
* @throws RuntimeException if ranges cannot be compared
|
||||||
|
*/
|
||||||
|
public boolean isOverlappedBy(final Range<T> otherRange) {
|
||||||
|
if (otherRange == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return otherRange.contains(minimum)
|
||||||
|
|| otherRange.contains(maximum)
|
||||||
|
|| contains(otherRange.minimum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether this range starts with the specified element.</p>
|
||||||
|
*
|
||||||
|
* @param element the element to check for, null returns false
|
||||||
|
* @return true if the specified element occurs within this range
|
||||||
|
*/
|
||||||
|
public boolean isStartedBy(final T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return comparator.compare(element, minimum) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Fits the given element into this range by returning the given element or, if out of bounds, the range minimum if
|
||||||
|
* below, or the range maximum if above.
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* Range<Integer> range = Range.between(16, 64);
|
||||||
|
* range.fit(-9) --> 16
|
||||||
|
* range.fit(0) --> 16
|
||||||
|
* range.fit(15) --> 16
|
||||||
|
* range.fit(16) --> 16
|
||||||
|
* range.fit(17) --> 17
|
||||||
|
* ...
|
||||||
|
* range.fit(63) --> 63
|
||||||
|
* range.fit(64) --> 64
|
||||||
|
* range.fit(99) --> 64
|
||||||
|
* </pre>
|
||||||
|
* @param element the element to check for, not null
|
||||||
|
* @return the minimum, the element, or the maximum depending on the element's location relative to the range
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public T fit(final T element) {
|
||||||
|
// Comparable API says throw NPE on null
|
||||||
|
Validate.notNull(element, "element");
|
||||||
|
if (isAfter(element)) {
|
||||||
|
return minimum;
|
||||||
|
} else if (isBefore(element)) {
|
||||||
|
return maximum;
|
||||||
|
} else {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the range as a {@code String}.</p>
|
||||||
|
*
|
||||||
|
* <p>The format of the String is '[<i>min</i>..<i>max</i>]'.</p>
|
||||||
|
*
|
||||||
|
* @return the {@code String} representation of this range
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (toString == null) {
|
||||||
|
toString = "[" + minimum + ".." + maximum + "]";
|
||||||
|
}
|
||||||
|
return toString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Formats the receiver using the given format.</p>
|
||||||
|
*
|
||||||
|
* <p>This uses {@link java.util.Formattable} to perform the formatting. Three variables may
|
||||||
|
* be used to embed the minimum, maximum and comparator.
|
||||||
|
* Use {@code %1$s} for the minimum element, {@code %2$s} for the maximum element
|
||||||
|
* and {@code %3$s} for the comparator.
|
||||||
|
* The default format used by {@code toString()} is {@code [%1$s..%2$s]}.</p>
|
||||||
|
*
|
||||||
|
* @param format the format string, optionally containing {@code %1$s}, {@code %2$s} and {@code %3$s}, not null
|
||||||
|
* @return the formatted string, not null
|
||||||
|
*/
|
||||||
|
public String toString(final String format) {
|
||||||
|
return String.format(format, minimum, maximum, comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
458
after/src/main/java/org/apache/commons/lang3/RegExUtils.java
Normal file
458
after/src/main/java/org/apache/commons/lang3/RegExUtils.java
Normal file
|
@ -0,0 +1,458 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Helpers to process Strings using regular expressions.</p>
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
* @since 3.8
|
||||||
|
*/
|
||||||
|
public class RegExUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Removes each substring of the text String that matches the given regular expression pattern.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code pattern.matcher(text).replaceAll(StringUtils.EMPTY)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.removeAll(null, *) = null
|
||||||
|
* StringUtils.removeAll("any", (Pattern) null) = "any"
|
||||||
|
* StringUtils.removeAll("any", Pattern.compile("")) = "any"
|
||||||
|
* StringUtils.removeAll("any", Pattern.compile(".*")) = ""
|
||||||
|
* StringUtils.removeAll("any", Pattern.compile(".+")) = ""
|
||||||
|
* StringUtils.removeAll("abc", Pattern.compile(".?")) = ""
|
||||||
|
* StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>")) = "A\nB"
|
||||||
|
* StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>")) = "AB"
|
||||||
|
* StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL)) = "AB"
|
||||||
|
* StringUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]")) = "ABC123"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to remove from, may be null
|
||||||
|
* @param regex the regular expression to which this string is to be matched
|
||||||
|
* @return the text with any removes processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @see #replaceAll(String, Pattern, String)
|
||||||
|
* @see java.util.regex.Matcher#replaceAll(String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
*/
|
||||||
|
public static String removeAll(final String text, final Pattern regex) {
|
||||||
|
return replaceAll(text, regex, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Removes each substring of the text String that matches the given regular expression.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text.replaceAll(regex, StringUtils.EMPTY)}</li>
|
||||||
|
* <li>{@code Pattern.compile(regex).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <p>Unlike in the {@link #removePattern(String, String)} method, the {@link Pattern#DOTALL} option
|
||||||
|
* is NOT automatically added.
|
||||||
|
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||||
|
* DOTALL is also known as single-line mode in Perl.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.removeAll(null, *) = null
|
||||||
|
* StringUtils.removeAll("any", (String) null) = "any"
|
||||||
|
* StringUtils.removeAll("any", "") = "any"
|
||||||
|
* StringUtils.removeAll("any", ".*") = ""
|
||||||
|
* StringUtils.removeAll("any", ".+") = ""
|
||||||
|
* StringUtils.removeAll("abc", ".?") = ""
|
||||||
|
* StringUtils.removeAll("A<__>\n<__>B", "<.*>") = "A\nB"
|
||||||
|
* StringUtils.removeAll("A<__>\n<__>B", "(?s)<.*>") = "AB"
|
||||||
|
* StringUtils.removeAll("ABCabc123abc", "[a-z]") = "ABC123"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to remove from, may be null
|
||||||
|
* @param regex the regular expression to which this string is to be matched
|
||||||
|
* @return the text with any removes processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @throws java.util.regex.PatternSyntaxException
|
||||||
|
* if the regular expression's syntax is invalid
|
||||||
|
*
|
||||||
|
* @see #replaceAll(String, String, String)
|
||||||
|
* @see #removePattern(String, String)
|
||||||
|
* @see String#replaceAll(String, String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
* @see java.util.regex.Pattern#DOTALL
|
||||||
|
*/
|
||||||
|
public static String removeAll(final String text, final String regex) {
|
||||||
|
return replaceAll(text, regex, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Removes the first substring of the text string that matches the given regular expression pattern.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.removeFirst(null, *) = null
|
||||||
|
* StringUtils.removeFirst("any", (Pattern) null) = "any"
|
||||||
|
* StringUtils.removeFirst("any", Pattern.compile("")) = "any"
|
||||||
|
* StringUtils.removeFirst("any", Pattern.compile(".*")) = ""
|
||||||
|
* StringUtils.removeFirst("any", Pattern.compile(".+")) = ""
|
||||||
|
* StringUtils.removeFirst("abc", Pattern.compile(".?")) = "bc"
|
||||||
|
* StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>")) = "A\n<__>B"
|
||||||
|
* StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>")) = "AB"
|
||||||
|
* StringUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]")) = "ABCbc123"
|
||||||
|
* StringUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+")) = "ABC123abc"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to remove from, may be null
|
||||||
|
* @param regex the regular expression pattern to which this string is to be matched
|
||||||
|
* @return the text with the first replacement processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @see #replaceFirst(String, Pattern, String)
|
||||||
|
* @see java.util.regex.Matcher#replaceFirst(String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
*/
|
||||||
|
public static String removeFirst(final String text, final Pattern regex) {
|
||||||
|
return replaceFirst(text, regex, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Removes the first substring of the text string that matches the given regular expression.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text.replaceFirst(regex, StringUtils.EMPTY)}</li>
|
||||||
|
* <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <p>The {@link Pattern#DOTALL} option is NOT automatically added.
|
||||||
|
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||||
|
* DOTALL is also known as single-line mode in Perl.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.removeFirst(null, *) = null
|
||||||
|
* StringUtils.removeFirst("any", (String) null) = "any"
|
||||||
|
* StringUtils.removeFirst("any", "") = "any"
|
||||||
|
* StringUtils.removeFirst("any", ".*") = ""
|
||||||
|
* StringUtils.removeFirst("any", ".+") = ""
|
||||||
|
* StringUtils.removeFirst("abc", ".?") = "bc"
|
||||||
|
* StringUtils.removeFirst("A<__>\n<__>B", "<.*>") = "A\n<__>B"
|
||||||
|
* StringUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>") = "AB"
|
||||||
|
* StringUtils.removeFirst("ABCabc123", "[a-z]") = "ABCbc123"
|
||||||
|
* StringUtils.removeFirst("ABCabc123abc", "[a-z]+") = "ABC123abc"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to remove from, may be null
|
||||||
|
* @param regex the regular expression to which this string is to be matched
|
||||||
|
* @return the text with the first replacement processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @throws java.util.regex.PatternSyntaxException
|
||||||
|
* if the regular expression's syntax is invalid
|
||||||
|
*
|
||||||
|
* @see #replaceFirst(String, String, String)
|
||||||
|
* @see String#replaceFirst(String, String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
* @see java.util.regex.Pattern#DOTALL
|
||||||
|
*/
|
||||||
|
public static String removeFirst(final String text, final String regex) {
|
||||||
|
return replaceFirst(text, regex, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Removes each substring of the source String that matches the given regular expression using the DOTALL option.</p>
|
||||||
|
*
|
||||||
|
* This call is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text.replaceAll("(?s)" + regex, StringUtils.EMPTY)}</li>
|
||||||
|
* <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.removePattern(null, *) = null
|
||||||
|
* StringUtils.removePattern("any", (String) null) = "any"
|
||||||
|
* StringUtils.removePattern("A<__>\n<__>B", "<.*>") = "AB"
|
||||||
|
* StringUtils.removePattern("ABCabc123", "[a-z]") = "ABC123"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* the source string
|
||||||
|
* @param regex
|
||||||
|
* the regular expression to which this string is to be matched
|
||||||
|
* @return The resulting {@code String}
|
||||||
|
* @see #replacePattern(String, String, String)
|
||||||
|
* @see String#replaceAll(String, String)
|
||||||
|
* @see Pattern#DOTALL
|
||||||
|
*/
|
||||||
|
public static String removePattern(final String text, final String regex) {
|
||||||
|
return replacePattern(text, regex, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.replaceAll(null, *, *) = null
|
||||||
|
* StringUtils.replaceAll("any", (Pattern) null, *) = "any"
|
||||||
|
* StringUtils.replaceAll("any", *, null) = "any"
|
||||||
|
* StringUtils.replaceAll("", Pattern.compile(""), "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceAll("", Pattern.compile(".*"), "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceAll("", Pattern.compile(".+"), "zzz") = ""
|
||||||
|
* StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ") = "ZZaZZbZZcZZ"
|
||||||
|
* StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z") = "z\nz"
|
||||||
|
* StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
|
||||||
|
* StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z") = "z"
|
||||||
|
* StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_") = "ABC___123"
|
||||||
|
* StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_") = "ABC_123"
|
||||||
|
* StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "") = "ABC123"
|
||||||
|
* StringUtils.replaceAll("Lorem ipsum dolor sit", Pattern.compile("( +)([a-z]+)"), "_$2") = "Lorem_ipsum_dolor_sit"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to search and replace in, may be null
|
||||||
|
* @param regex the regular expression pattern to which this string is to be matched
|
||||||
|
* @param replacement the string to be substituted for each match
|
||||||
|
* @return the text with any replacements processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @see java.util.regex.Matcher#replaceAll(String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
*/
|
||||||
|
public static String replaceAll(final String text, final Pattern regex, final String replacement) {
|
||||||
|
if (ObjectUtils.anyNull(text, regex, replacement)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return regex.matcher(text).replaceAll(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces each substring of the text String that matches the given regular expression
|
||||||
|
* with the given replacement.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text.replaceAll(regex, replacement)}</li>
|
||||||
|
* <li>{@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <p>Unlike in the {@link #replacePattern(String, String, String)} method, the {@link Pattern#DOTALL} option
|
||||||
|
* is NOT automatically added.
|
||||||
|
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||||
|
* DOTALL is also known as single-line mode in Perl.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.replaceAll(null, *, *) = null
|
||||||
|
* StringUtils.replaceAll("any", (String) null, *) = "any"
|
||||||
|
* StringUtils.replaceAll("any", *, null) = "any"
|
||||||
|
* StringUtils.replaceAll("", "", "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceAll("", ".*", "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceAll("", ".+", "zzz") = ""
|
||||||
|
* StringUtils.replaceAll("abc", "", "ZZ") = "ZZaZZbZZcZZ"
|
||||||
|
* StringUtils.replaceAll("<__>\n<__>", "<.*>", "z") = "z\nz"
|
||||||
|
* StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z") = "z"
|
||||||
|
* StringUtils.replaceAll("ABCabc123", "[a-z]", "_") = "ABC___123"
|
||||||
|
* StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_") = "ABC_123"
|
||||||
|
* StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "") = "ABC123"
|
||||||
|
* StringUtils.replaceAll("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2") = "Lorem_ipsum_dolor_sit"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to search and replace in, may be null
|
||||||
|
* @param regex the regular expression to which this string is to be matched
|
||||||
|
* @param replacement the string to be substituted for each match
|
||||||
|
* @return the text with any replacements processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @throws java.util.regex.PatternSyntaxException
|
||||||
|
* if the regular expression's syntax is invalid
|
||||||
|
*
|
||||||
|
* @see #replacePattern(String, String, String)
|
||||||
|
* @see String#replaceAll(String, String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
* @see java.util.regex.Pattern#DOTALL
|
||||||
|
*/
|
||||||
|
public static String replaceAll(final String text, final String regex, final String replacement) {
|
||||||
|
if (ObjectUtils.anyNull(text, regex, replacement)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return text.replaceAll(regex, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces the first substring of the text string that matches the given regular expression pattern
|
||||||
|
* with the given replacement.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.replaceFirst(null, *, *) = null
|
||||||
|
* StringUtils.replaceFirst("any", (Pattern) null, *) = "any"
|
||||||
|
* StringUtils.replaceFirst("any", *, null) = "any"
|
||||||
|
* StringUtils.replaceFirst("", Pattern.compile(""), "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz") = ""
|
||||||
|
* StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ") = "ZZabc"
|
||||||
|
* StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z") = "z\n<__>"
|
||||||
|
* StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z") = "z"
|
||||||
|
* StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_") = "ABC_bc123"
|
||||||
|
* StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_") = "ABC_123abc"
|
||||||
|
* StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "") = "ABC123abc"
|
||||||
|
* StringUtils.replaceFirst("Lorem ipsum dolor sit", Pattern.compile("( +)([a-z]+)"), "_$2") = "Lorem_ipsum dolor sit"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to search and replace in, may be null
|
||||||
|
* @param regex the regular expression pattern to which this string is to be matched
|
||||||
|
* @param replacement the string to be substituted for the first match
|
||||||
|
* @return the text with the first replacement processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @see java.util.regex.Matcher#replaceFirst(String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
*/
|
||||||
|
public static String replaceFirst(final String text, final Pattern regex, final String replacement) {
|
||||||
|
if (text == null || regex == null|| replacement == null ) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return regex.matcher(text).replaceFirst(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces the first substring of the text string that matches the given regular expression
|
||||||
|
* with the given replacement.</p>
|
||||||
|
*
|
||||||
|
* This method is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text.replaceFirst(regex, replacement)}</li>
|
||||||
|
* <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <p>The {@link Pattern#DOTALL} option is NOT automatically added.
|
||||||
|
* To use the DOTALL option prepend {@code "(?s)"} to the regex.
|
||||||
|
* DOTALL is also known as single-line mode in Perl.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.replaceFirst(null, *, *) = null
|
||||||
|
* StringUtils.replaceFirst("any", (String) null, *) = "any"
|
||||||
|
* StringUtils.replaceFirst("any", *, null) = "any"
|
||||||
|
* StringUtils.replaceFirst("", "", "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceFirst("", ".*", "zzz") = "zzz"
|
||||||
|
* StringUtils.replaceFirst("", ".+", "zzz") = ""
|
||||||
|
* StringUtils.replaceFirst("abc", "", "ZZ") = "ZZabc"
|
||||||
|
* StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z") = "z\n<__>"
|
||||||
|
* StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z") = "z"
|
||||||
|
* StringUtils.replaceFirst("ABCabc123", "[a-z]", "_") = "ABC_bc123"
|
||||||
|
* StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_") = "ABC_123abc"
|
||||||
|
* StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "") = "ABC123abc"
|
||||||
|
* StringUtils.replaceFirst("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2") = "Lorem_ipsum dolor sit"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text text to search and replace in, may be null
|
||||||
|
* @param regex the regular expression to which this string is to be matched
|
||||||
|
* @param replacement the string to be substituted for the first match
|
||||||
|
* @return the text with the first replacement processed,
|
||||||
|
* {@code null} if null String input
|
||||||
|
*
|
||||||
|
* @throws java.util.regex.PatternSyntaxException
|
||||||
|
* if the regular expression's syntax is invalid
|
||||||
|
*
|
||||||
|
* @see String#replaceFirst(String, String)
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
* @see java.util.regex.Pattern#DOTALL
|
||||||
|
*/
|
||||||
|
public static String replaceFirst(final String text, final String regex, final String replacement) {
|
||||||
|
if (text == null || regex == null|| replacement == null ) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return text.replaceFirst(regex, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Replaces each substring of the source String that matches the given regular expression with the given
|
||||||
|
* replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.</p>
|
||||||
|
*
|
||||||
|
* This call is a {@code null} safe equivalent to:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text.replaceAll("(?s)" + regex, replacement)}</li>
|
||||||
|
* <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.replacePattern(null, *, *) = null
|
||||||
|
* StringUtils.replacePattern("any", (String) null, *) = "any"
|
||||||
|
* StringUtils.replacePattern("any", *, null) = "any"
|
||||||
|
* StringUtils.replacePattern("", "", "zzz") = "zzz"
|
||||||
|
* StringUtils.replacePattern("", ".*", "zzz") = "zzz"
|
||||||
|
* StringUtils.replacePattern("", ".+", "zzz") = ""
|
||||||
|
* StringUtils.replacePattern("<__>\n<__>", "<.*>", "z") = "z"
|
||||||
|
* StringUtils.replacePattern("ABCabc123", "[a-z]", "_") = "ABC___123"
|
||||||
|
* StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_") = "ABC_123"
|
||||||
|
* StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "") = "ABC123"
|
||||||
|
* StringUtils.replacePattern("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2") = "Lorem_ipsum_dolor_sit"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* the source string
|
||||||
|
* @param regex
|
||||||
|
* the regular expression to which this string is to be matched
|
||||||
|
* @param replacement
|
||||||
|
* the string to be substituted for each match
|
||||||
|
* @return The resulting {@code String}
|
||||||
|
* @see #replaceAll(String, String, String)
|
||||||
|
* @see String#replaceAll(String, String)
|
||||||
|
* @see Pattern#DOTALL
|
||||||
|
*/
|
||||||
|
public static String replacePattern(final String text, final String regex, final String replacement) {
|
||||||
|
if (ObjectUtils.anyNull(text, regex, replacement)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Exception thrown when the Serialization process fails.</p>
|
||||||
|
*
|
||||||
|
* <p>The original error is wrapped within this one.</p>
|
||||||
|
*
|
||||||
|
* <p>#NotThreadSafe# because Throwable is not thread-safe</p>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class SerializationException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support.
|
||||||
|
*
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 4029025366392702726L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a new {@code SerializationException} without specified
|
||||||
|
* detail message.</p>
|
||||||
|
*/
|
||||||
|
public SerializationException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a new {@code SerializationException} with specified
|
||||||
|
* detail message.</p>
|
||||||
|
*
|
||||||
|
* @param msg The error message.
|
||||||
|
*/
|
||||||
|
public SerializationException(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a new {@code SerializationException} with specified
|
||||||
|
* nested {@code Throwable}.</p>
|
||||||
|
*
|
||||||
|
* @param cause The {@code Exception} or {@code Error}
|
||||||
|
* that caused this exception to be thrown.
|
||||||
|
*/
|
||||||
|
public SerializationException(final Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructs a new {@code SerializationException} with specified
|
||||||
|
* detail message and nested {@code Throwable}.</p>
|
||||||
|
*
|
||||||
|
* @param msg The error message.
|
||||||
|
* @param cause The {@code Exception} or {@code Error}
|
||||||
|
* that caused this exception to be thrown.
|
||||||
|
*/
|
||||||
|
public SerializationException(final String msg, final Throwable cause) {
|
||||||
|
super(msg, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.ObjectStreamClass;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Assists with the serialization process and performs additional functionality based
|
||||||
|
* on serialization.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Deep clone using serialization
|
||||||
|
* <li>Serialize managing finally and IOException
|
||||||
|
* <li>Deserialize managing finally and IOException
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>This class throws exceptions for invalid {@code null} inputs.
|
||||||
|
* Each method documents its behavior in more detail.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class SerializationUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Custom specialization of the standard JDK {@link java.io.ObjectInputStream}
|
||||||
|
* that uses a custom {@code ClassLoader} to resolve a class.
|
||||||
|
* If the specified {@code ClassLoader} is not able to resolve the class,
|
||||||
|
* the context classloader of the current thread will be used.
|
||||||
|
* This way, the standard deserialization work also in web-application
|
||||||
|
* containers and application servers, no matter in which of the
|
||||||
|
* {@code ClassLoader} the particular class that encapsulates
|
||||||
|
* serialization/deserialization lives. </p>
|
||||||
|
*
|
||||||
|
* <p>For more in-depth information about the problem for which this
|
||||||
|
* class here is a workaround, see the JIRA issue LANG-626. </p>
|
||||||
|
*/
|
||||||
|
static class ClassLoaderAwareObjectInputStream extends ObjectInputStream {
|
||||||
|
private static final Map<String, Class<?>> primitiveTypes =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
primitiveTypes.put("byte", byte.class);
|
||||||
|
primitiveTypes.put("short", short.class);
|
||||||
|
primitiveTypes.put("int", int.class);
|
||||||
|
primitiveTypes.put("long", long.class);
|
||||||
|
primitiveTypes.put("float", float.class);
|
||||||
|
primitiveTypes.put("double", double.class);
|
||||||
|
primitiveTypes.put("boolean", boolean.class);
|
||||||
|
primitiveTypes.put("char", char.class);
|
||||||
|
primitiveTypes.put("void", void.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param in The {@code InputStream}.
|
||||||
|
* @param classLoader classloader to use
|
||||||
|
* @throws IOException if an I/O error occurs while reading stream header.
|
||||||
|
* @see java.io.ObjectInputStream
|
||||||
|
*/
|
||||||
|
ClassLoaderAwareObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
|
||||||
|
super(in);
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden version that uses the parameterized {@code ClassLoader} or the {@code ClassLoader}
|
||||||
|
* of the current {@code Thread} to resolve the class.
|
||||||
|
* @param desc An instance of class {@code ObjectStreamClass}.
|
||||||
|
* @return A {@code Class} object corresponding to {@code desc}.
|
||||||
|
* @throws IOException Any of the usual Input/Output exceptions.
|
||||||
|
* @throws ClassNotFoundException If class of a serialized object cannot be found.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException {
|
||||||
|
final String name = desc.getName();
|
||||||
|
try {
|
||||||
|
return Class.forName(name, false, classLoader);
|
||||||
|
} catch (final ClassNotFoundException ex) {
|
||||||
|
try {
|
||||||
|
return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
|
||||||
|
} catch (final ClassNotFoundException cnfe) {
|
||||||
|
final Class<?> cls = primitiveTypes.get(name);
|
||||||
|
if (cls != null) {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
throw cnfe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Deep clone an {@code Object} using serialization.</p>
|
||||||
|
*
|
||||||
|
* <p>This is many times slower than writing clone methods by hand
|
||||||
|
* on all objects in your object graph. However, for complex object
|
||||||
|
* graphs, or for those that don't support deep cloning this can
|
||||||
|
* be a simple alternative implementation. Of course all the objects
|
||||||
|
* must be {@code Serializable}.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the object involved
|
||||||
|
* @param object the {@code Serializable} object to clone
|
||||||
|
* @return the cloned object
|
||||||
|
* @throws SerializationException (runtime) if the serialization fails
|
||||||
|
*/
|
||||||
|
public static <T extends Serializable> T clone(final T object) {
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final byte[] objectData = serialize(object);
|
||||||
|
final ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
|
||||||
|
|
||||||
|
try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais,
|
||||||
|
object.getClass().getClassLoader())) {
|
||||||
|
/*
|
||||||
|
* when we serialize and deserialize an object,
|
||||||
|
* it is reasonable to assume the deserialized object
|
||||||
|
* is of the same type as the original serialized object
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked") // see above
|
||||||
|
final T readObject = (T) in.readObject();
|
||||||
|
return readObject;
|
||||||
|
|
||||||
|
} catch (final ClassNotFoundException ex) {
|
||||||
|
throw new SerializationException("ClassNotFoundException while reading cloned object data", ex);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new SerializationException("IOException while reading or closing cloned object data", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Deserializes a single {@code Object} from an array of bytes.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site.
|
||||||
|
* Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException.
|
||||||
|
* Note that in both cases, the ClassCastException is in the call site, not in this method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T> the object type to be deserialized
|
||||||
|
* @param objectData
|
||||||
|
* the serialized object, must not be null
|
||||||
|
* @return the deserialized object
|
||||||
|
* @throws NullPointerException if {@code objectData} is {@code null}
|
||||||
|
* @throws SerializationException (runtime) if the serialization fails
|
||||||
|
*/
|
||||||
|
public static <T> T deserialize(final byte[] objectData) {
|
||||||
|
Validate.notNull(objectData, "objectData");
|
||||||
|
return deserialize(new ByteArrayInputStream(objectData));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Deserializes an {@code Object} from the specified stream.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The stream will be closed once the object is written. This avoids the need for a finally clause, and maybe also
|
||||||
|
* exception handling, in the application code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The stream passed in is not buffered internally within this method. This is the responsibility of your
|
||||||
|
* application if desired.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site.
|
||||||
|
* Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException.
|
||||||
|
* Note that in both cases, the ClassCastException is in the call site, not in this method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T> the object type to be deserialized
|
||||||
|
* @param inputStream
|
||||||
|
* the serialized object input stream, must not be null
|
||||||
|
* @return the deserialized object
|
||||||
|
* @throws NullPointerException if {@code inputStream} is {@code null}
|
||||||
|
* @throws SerializationException (runtime) if the serialization fails
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("resource") // inputStream is managed by the caller
|
||||||
|
public static <T> T deserialize(final InputStream inputStream) {
|
||||||
|
Validate.notNull(inputStream, "inputStream");
|
||||||
|
try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final T obj = (T) in.readObject();
|
||||||
|
return obj;
|
||||||
|
} catch (final ClassNotFoundException | IOException ex) {
|
||||||
|
throw new SerializationException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a serialization roundtrip. Serializes and deserializes the given object, great for testing objects that
|
||||||
|
* implement {@link Serializable}.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object involved
|
||||||
|
* @param obj
|
||||||
|
* the object to roundtrip
|
||||||
|
* @return the serialized and deserialized object
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked") // OK, because we serialized a type `T`
|
||||||
|
public static <T extends Serializable> T roundtrip(final T obj) {
|
||||||
|
return (T) deserialize(serialize(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Serializes an {@code Object} to a byte array for
|
||||||
|
* storage/serialization.</p>
|
||||||
|
*
|
||||||
|
* @param obj the object to serialize to bytes
|
||||||
|
* @return a byte[] with the converted Serializable
|
||||||
|
* @throws SerializationException (runtime) if the serialization fails
|
||||||
|
*/
|
||||||
|
public static byte[] serialize(final Serializable obj) {
|
||||||
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||||
|
serialize(obj, baos);
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Serializes an {@code Object} to the specified stream.</p>
|
||||||
|
*
|
||||||
|
* <p>The stream will be closed once the object is written.
|
||||||
|
* This avoids the need for a finally clause, and maybe also exception
|
||||||
|
* handling, in the application code.</p>
|
||||||
|
*
|
||||||
|
* <p>The stream passed in is not buffered internally within this method.
|
||||||
|
* This is the responsibility of your application if desired.</p>
|
||||||
|
*
|
||||||
|
* @param obj the object to serialize to bytes, may be null
|
||||||
|
* @param outputStream the stream to write to, must not be null
|
||||||
|
* @throws NullPointerException if {@code outputStream} is {@code null}
|
||||||
|
* @throws SerializationException (runtime) if the serialization fails
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("resource") // outputStream is managed by the caller
|
||||||
|
public static void serialize(final Serializable obj, final OutputStream outputStream) {
|
||||||
|
Validate.notNull(outputStream, "outputStream");
|
||||||
|
try (ObjectOutputStream out = new ObjectOutputStream(outputStream)) {
|
||||||
|
out.writeObject(obj);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new SerializationException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>SerializationUtils instances should NOT be constructed in standard programming.
|
||||||
|
* Instead, the class should be used as {@code SerializationUtils.clone(object)}.</p>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean instance
|
||||||
|
* to operate.</p>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public SerializationUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
507
after/src/main/java/org/apache/commons/lang3/Streams.java
Normal file
507
after/src/main/java/org/apache/commons/lang3/Streams.java
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Functions.FailableConsumer;
|
||||||
|
import org.apache.commons.lang3.Functions.FailableFunction;
|
||||||
|
import org.apache.commons.lang3.Functions.FailablePredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides utility functions, and classes for working with the
|
||||||
|
* {@code java.util.stream} package, or more generally, with Java 8 lambdas. More
|
||||||
|
* specifically, it attempts to address the fact that lambdas are supposed
|
||||||
|
* not to throw Exceptions, at least not checked Exceptions, AKA instances
|
||||||
|
* of {@link Exception}. This enforces the use of constructs like
|
||||||
|
* <pre>
|
||||||
|
* Consumer<java.lang.reflect.Method> consumer = m -> {
|
||||||
|
* try {
|
||||||
|
* m.invoke(o, args);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* throw Functions.rethrow(t);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* stream.forEach(consumer);
|
||||||
|
* </pre>
|
||||||
|
* Using a {@link FailableStream}, this can be rewritten as follows:
|
||||||
|
* <pre>
|
||||||
|
* Streams.failable(stream).forEach((m) -> m.invoke(o, args));
|
||||||
|
* </pre>
|
||||||
|
* Obviously, the second version is much more concise and the spirit of
|
||||||
|
* Lambda expressions is met better than in the first version.
|
||||||
|
*
|
||||||
|
* @see Stream
|
||||||
|
* @see Functions
|
||||||
|
* @since 3.10
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class Streams {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reduced, and simplified version of a {@link Stream} with
|
||||||
|
* failable method signatures.
|
||||||
|
* @param <O> The streams element type.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams.FailableStream}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static class FailableStream<O extends Object> {
|
||||||
|
|
||||||
|
private Stream<O> stream;
|
||||||
|
private boolean terminated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance with the given {@code stream}.
|
||||||
|
* @param stream The stream.
|
||||||
|
*/
|
||||||
|
public FailableStream(final Stream<O> stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertNotTerminated() {
|
||||||
|
if (terminated) {
|
||||||
|
throw new IllegalStateException("This stream is already terminated.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void makeTerminated() {
|
||||||
|
assertNotTerminated();
|
||||||
|
terminated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a FailableStream consisting of the elements of this stream that match
|
||||||
|
* the given FailablePredicate.
|
||||||
|
*
|
||||||
|
* <p>This is an intermediate operation.
|
||||||
|
*
|
||||||
|
* @param predicate a non-interfering, stateless predicate to apply to each
|
||||||
|
* element to determine if it should be included.
|
||||||
|
* @return the new stream
|
||||||
|
*/
|
||||||
|
public FailableStream<O> filter(final FailablePredicate<O, ?> predicate){
|
||||||
|
assertNotTerminated();
|
||||||
|
stream = stream.filter(Functions.asPredicate(predicate));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an action for each element of this stream.
|
||||||
|
*
|
||||||
|
* <p>This is a terminal operation.
|
||||||
|
*
|
||||||
|
* <p>The behavior of this operation is explicitly nondeterministic.
|
||||||
|
* For parallel stream pipelines, this operation does <em>not</em>
|
||||||
|
* guarantee to respect the encounter order of the stream, as doing so
|
||||||
|
* would sacrifice the benefit of parallelism. For any given element, the
|
||||||
|
* action may be performed at whatever time and in whatever thread the
|
||||||
|
* library chooses. If the action accesses shared state, it is
|
||||||
|
* responsible for providing the required synchronization.
|
||||||
|
*
|
||||||
|
* @param action a non-interfering action to perform on the elements
|
||||||
|
*/
|
||||||
|
public void forEach(final FailableConsumer<O, ?> action) {
|
||||||
|
makeTerminated();
|
||||||
|
stream().forEach(Functions.asConsumer(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a mutable reduction operation on the elements of this stream using a
|
||||||
|
* {@code Collector}. A {@code Collector}
|
||||||
|
* encapsulates the functions used as arguments to
|
||||||
|
* {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of
|
||||||
|
* collection strategies and composition of collect operations such as
|
||||||
|
* multiple-level grouping or partitioning.
|
||||||
|
*
|
||||||
|
* <p>If the underlying stream is parallel, and the {@code Collector}
|
||||||
|
* is concurrent, and either the stream is unordered or the collector is
|
||||||
|
* unordered, then a concurrent reduction will be performed
|
||||||
|
* (see {@link Collector} for details on concurrent reduction.)
|
||||||
|
*
|
||||||
|
* <p>This is a terminal operation.
|
||||||
|
*
|
||||||
|
* <p>When executed in parallel, multiple intermediate results may be
|
||||||
|
* instantiated, populated, and merged so as to maintain isolation of
|
||||||
|
* mutable data structures. Therefore, even when executed in parallel
|
||||||
|
* with non-thread-safe data structures (such as {@code ArrayList}), no
|
||||||
|
* additional synchronization is needed for a parallel reduction.
|
||||||
|
*
|
||||||
|
* Note
|
||||||
|
* The following will accumulate strings into an ArrayList:
|
||||||
|
* <pre>{@code
|
||||||
|
* List<String> asList = stringStream.collect(Collectors.toList());
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>The following will classify {@code Person} objects by city:
|
||||||
|
* <pre>{@code
|
||||||
|
* Map<String, List<Person>> peopleByCity
|
||||||
|
* = personStream.collect(Collectors.groupingBy(Person::getCity));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>The following will classify {@code Person} objects by state and city,
|
||||||
|
* cascading two {@code Collector}s together:
|
||||||
|
* <pre>{@code
|
||||||
|
* Map<String, Map<String, List<Person>>> peopleByStateAndCity
|
||||||
|
* = personStream.collect(Collectors.groupingBy(Person::getState,
|
||||||
|
* Collectors.groupingBy(Person::getCity)));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param <R> the type of the result
|
||||||
|
* @param <A> the intermediate accumulation type of the {@code Collector}
|
||||||
|
* @param collector the {@code Collector} describing the reduction
|
||||||
|
* @return the result of the reduction
|
||||||
|
* @see #collect(Supplier, BiConsumer, BiConsumer)
|
||||||
|
* @see Collectors
|
||||||
|
*/
|
||||||
|
public <A, R> R collect(final Collector<? super O, A, R> collector) {
|
||||||
|
makeTerminated();
|
||||||
|
return stream().collect(collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a mutable reduction operation on the elements of this FailableStream.
|
||||||
|
* A mutable reduction is one in which the reduced value is a mutable result
|
||||||
|
* container, such as an {@code ArrayList}, and elements are incorporated by updating
|
||||||
|
* the state of the result rather than by replacing the result. This produces a result equivalent to:
|
||||||
|
* <pre>{@code
|
||||||
|
* R result = supplier.get();
|
||||||
|
* for (T element : this stream)
|
||||||
|
* accumulator.accept(result, element);
|
||||||
|
* return result;
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations
|
||||||
|
* can be parallelized without requiring additional synchronization.
|
||||||
|
*
|
||||||
|
* <p>This is a terminal operation.
|
||||||
|
*
|
||||||
|
* Note There are many existing classes in the JDK whose signatures are
|
||||||
|
* well-suited for use with method references as arguments to {@code collect()}.
|
||||||
|
* For example, the following will accumulate strings into an {@code ArrayList}:
|
||||||
|
* <pre>{@code
|
||||||
|
* List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add,
|
||||||
|
* ArrayList::addAll);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>The following will take a stream of strings and concatenates them into a
|
||||||
|
* single string:
|
||||||
|
* <pre>{@code
|
||||||
|
* String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
|
||||||
|
* StringBuilder::append)
|
||||||
|
* .toString();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param <R> type of the result
|
||||||
|
* @param <A> Type of the accumulator.
|
||||||
|
* @param pupplier a function that creates a new result container. For a
|
||||||
|
* parallel execution, this function may be called
|
||||||
|
* multiple times and must return a fresh value each time.
|
||||||
|
* @param accumulator An associative, non-interfering, stateless function for
|
||||||
|
* incorporating an additional element into a result
|
||||||
|
* @param combiner An associative, non-interfering, stateless
|
||||||
|
* function for combining two values, which must be compatible with the
|
||||||
|
* accumulator function
|
||||||
|
* @return The result of the reduction
|
||||||
|
*/
|
||||||
|
public <A, R> R collect(final Supplier<R> pupplier, final BiConsumer<R, ? super O> accumulator, final BiConsumer<R, R> combiner) {
|
||||||
|
makeTerminated();
|
||||||
|
return stream().collect(pupplier, accumulator, combiner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a reduction on the elements of this stream, using the provided
|
||||||
|
* identity value and an associative accumulation function, and returns
|
||||||
|
* the reduced value. This is equivalent to:
|
||||||
|
* <pre>{@code
|
||||||
|
* T result = identity;
|
||||||
|
* for (T element : this stream)
|
||||||
|
* result = accumulator.apply(result, element)
|
||||||
|
* return result;
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* but is not constrained to execute sequentially.
|
||||||
|
*
|
||||||
|
* <p>The {@code identity} value must be an identity for the accumulator
|
||||||
|
* function. This means that for all {@code t},
|
||||||
|
* {@code accumulator.apply(identity, t)} is equal to {@code t}.
|
||||||
|
* The {@code accumulator} function must be an associative function.
|
||||||
|
*
|
||||||
|
* <p>This is a terminal operation.
|
||||||
|
*
|
||||||
|
* Note Sum, min, max, average, and string concatenation are all special
|
||||||
|
* cases of reduction. Summing a stream of numbers can be expressed as:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Integer sum = integers.reduce(0, (a, b) -> a+b);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* or:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Integer sum = integers.reduce(0, Integer::sum);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>While this may seem a more roundabout way to perform an aggregation
|
||||||
|
* compared to simply mutating a running total in a loop, reduction
|
||||||
|
* operations parallelize more gracefully, without needing additional
|
||||||
|
* synchronization and with greatly reduced risk of data races.
|
||||||
|
*
|
||||||
|
* @param identity the identity value for the accumulating function
|
||||||
|
* @param accumulator an associative, non-interfering, stateless
|
||||||
|
* function for combining two values
|
||||||
|
* @return the result of the reduction
|
||||||
|
*/
|
||||||
|
public O reduce(final O identity, final BinaryOperator<O> accumulator) {
|
||||||
|
makeTerminated();
|
||||||
|
return stream().reduce(identity, accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stream consisting of the results of applying the given
|
||||||
|
* function to the elements of this stream.
|
||||||
|
*
|
||||||
|
* <p>This is an intermediate operation.
|
||||||
|
*
|
||||||
|
* @param <R> The element type of the new stream
|
||||||
|
* @param mapper A non-interfering, stateless function to apply to each element
|
||||||
|
* @return the new stream
|
||||||
|
*/
|
||||||
|
public <R> FailableStream<R> map(final FailableFunction<O, R, ?> mapper) {
|
||||||
|
assertNotTerminated();
|
||||||
|
return new FailableStream<>(stream.map(Functions.asFunction(mapper)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the FailableStream into an equivalent stream.
|
||||||
|
* @return A stream, which will return the same elements, which this FailableStream would return.
|
||||||
|
*/
|
||||||
|
public Stream<O> stream() {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether all elements of this stream match the provided predicate.
|
||||||
|
* May not evaluate the predicate on all elements if not necessary for
|
||||||
|
* determining the result. If the stream is empty then {@code true} is
|
||||||
|
* returned and the predicate is not evaluated.
|
||||||
|
*
|
||||||
|
* <p>This is a short-circuiting terminal operation.
|
||||||
|
*
|
||||||
|
* Note
|
||||||
|
* This method evaluates the <em>universal quantification</em> of the
|
||||||
|
* predicate over the elements of the stream (for all x P(x)). If the
|
||||||
|
* stream is empty, the quantification is said to be <em>vacuously
|
||||||
|
* satisfied</em> and is always {@code true} (regardless of P(x)).
|
||||||
|
*
|
||||||
|
* @param predicate A non-interfering, stateless predicate to apply to
|
||||||
|
* elements of this stream
|
||||||
|
* @return {@code true} If either all elements of the stream match the
|
||||||
|
* provided predicate or the stream is empty, otherwise {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean allMatch(final FailablePredicate<O, ?> predicate) {
|
||||||
|
assertNotTerminated();
|
||||||
|
return stream().allMatch(Functions.asPredicate(predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether any elements of this stream match the provided
|
||||||
|
* predicate. May not evaluate the predicate on all elements if not
|
||||||
|
* necessary for determining the result. If the stream is empty then
|
||||||
|
* {@code false} is returned and the predicate is not evaluated.
|
||||||
|
*
|
||||||
|
* <p>This is a short-circuiting terminal operation.
|
||||||
|
*
|
||||||
|
* Note
|
||||||
|
* This method evaluates the <em>existential quantification</em> of the
|
||||||
|
* predicate over the elements of the stream (for some x P(x)).
|
||||||
|
*
|
||||||
|
* @param predicate A non-interfering, stateless predicate to apply to
|
||||||
|
* elements of this stream
|
||||||
|
* @return {@code true} if any elements of the stream match the provided
|
||||||
|
* predicate, otherwise {@code false}
|
||||||
|
*/
|
||||||
|
public boolean anyMatch(final FailablePredicate<O, ?> predicate) {
|
||||||
|
assertNotTerminated();
|
||||||
|
return stream().anyMatch(Functions.asPredicate(predicate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link Stream stream} into a {@link FailableStream}.
|
||||||
|
* This is basically a simplified, reduced version of the {@link Stream}
|
||||||
|
* class, with the same underlying element stream, except that failable
|
||||||
|
* objects, like {@link FailablePredicate}, {@link FailableFunction}, or
|
||||||
|
* {@link FailableConsumer} may be applied, instead of
|
||||||
|
* {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
|
||||||
|
* to rewrite a code snippet like this:
|
||||||
|
* <pre>
|
||||||
|
* final List<O> list;
|
||||||
|
* final Method m;
|
||||||
|
* final Function<O,String> mapper = (o) -> {
|
||||||
|
* try {
|
||||||
|
* return (String) m.invoke(o);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* throw Functions.rethrow(t);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* final List<String> strList = list.stream()
|
||||||
|
* .map(mapper).collect(Collectors.toList());
|
||||||
|
* </pre>
|
||||||
|
* as follows:
|
||||||
|
* <pre>
|
||||||
|
* final List<O> list;
|
||||||
|
* final Method m;
|
||||||
|
* final List<String> strList = Functions.stream(list.stream())
|
||||||
|
* .map((o) -> (String) m.invoke(o)).collect(Collectors.toList());
|
||||||
|
* </pre>
|
||||||
|
* While the second version may not be <em>quite</em> as
|
||||||
|
* efficient (because it depends on the creation of additional,
|
||||||
|
* intermediate objects, of type FailableStream), it is much more
|
||||||
|
* concise, and readable, and meets the spirit of Lambdas better
|
||||||
|
* than the first version.
|
||||||
|
* @param <O> The streams element type.
|
||||||
|
* @param stream The stream, which is being converted.
|
||||||
|
* @return The {@link FailableStream}, which has been created by
|
||||||
|
* converting the stream.
|
||||||
|
*/
|
||||||
|
public static <O> FailableStream<O> stream(final Stream<O> stream) {
|
||||||
|
return new FailableStream<>(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given {@link Collection} into a {@link FailableStream}.
|
||||||
|
* This is basically a simplified, reduced version of the {@link Stream}
|
||||||
|
* class, with the same underlying element stream, except that failable
|
||||||
|
* objects, like {@link FailablePredicate}, {@link FailableFunction}, or
|
||||||
|
* {@link FailableConsumer} may be applied, instead of
|
||||||
|
* {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
|
||||||
|
* to rewrite a code snippet like this:
|
||||||
|
* <pre>
|
||||||
|
* final List<O> list;
|
||||||
|
* final Method m;
|
||||||
|
* final Function<O,String> mapper = (o) -> {
|
||||||
|
* try {
|
||||||
|
* return (String) m.invoke(o);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* throw Functions.rethrow(t);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* final List<String> strList = list.stream()
|
||||||
|
* .map(mapper).collect(Collectors.toList());
|
||||||
|
* </pre>
|
||||||
|
* as follows:
|
||||||
|
* <pre>
|
||||||
|
* final List<O> list;
|
||||||
|
* final Method m;
|
||||||
|
* final List<String> strList = Functions.stream(list.stream())
|
||||||
|
* .map((o) -> (String) m.invoke(o)).collect(Collectors.toList());
|
||||||
|
* </pre>
|
||||||
|
* While the second version may not be <em>quite</em> as
|
||||||
|
* efficient (because it depends on the creation of additional,
|
||||||
|
* intermediate objects, of type FailableStream), it is much more
|
||||||
|
* concise, and readable, and meets the spirit of Lambdas better
|
||||||
|
* than the first version.
|
||||||
|
* @param <O> The streams element type.
|
||||||
|
* @param stream The stream, which is being converted.
|
||||||
|
* @return The {@link FailableStream}, which has been created by
|
||||||
|
* converting the stream.
|
||||||
|
*/
|
||||||
|
public static <O> FailableStream<O> stream(final Collection<O> stream) {
|
||||||
|
return stream(stream.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Collector type for arrays.
|
||||||
|
*
|
||||||
|
* @param <O> The array type.
|
||||||
|
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams.ArrayCollector}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static class ArrayCollector<O> implements Collector<O, List<O>, O[]> {
|
||||||
|
private static final Set<Characteristics> characteristics = Collections.emptySet();
|
||||||
|
private final Class<O> elementType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance for the given element type.
|
||||||
|
*
|
||||||
|
* @param elementType The element type.
|
||||||
|
*/
|
||||||
|
public ArrayCollector(final Class<O> elementType) {
|
||||||
|
this.elementType = elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Supplier<List<O>> supplier() {
|
||||||
|
return ArrayList::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiConsumer<List<O>, O> accumulator() {
|
||||||
|
return List::add;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryOperator<List<O>> combiner() {
|
||||||
|
return (left, right) -> {
|
||||||
|
left.addAll(right);
|
||||||
|
return left;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<List<O>, O[]> finisher() {
|
||||||
|
return list -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final O[] array = (O[]) Array.newInstance(elementType, list.size());
|
||||||
|
return list.toArray(array);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Characteristics> characteristics() {
|
||||||
|
return characteristics;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Collector} that accumulates the input elements into a
|
||||||
|
* new array.
|
||||||
|
*
|
||||||
|
* @param pElementType Type of an element in the array.
|
||||||
|
* @param <O> the type of the input elements
|
||||||
|
* @return a {@code Collector} which collects all the input elements into an
|
||||||
|
* array, in encounter order
|
||||||
|
*/
|
||||||
|
public static <O extends Object> Collector<O, ?, O[]> toArray(final Class<O> pElementType) {
|
||||||
|
return new ArrayCollector<>(pElementType);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,805 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.text.translate.AggregateTranslator;
|
||||||
|
import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
|
||||||
|
import org.apache.commons.lang3.text.translate.EntityArrays;
|
||||||
|
import org.apache.commons.lang3.text.translate.JavaUnicodeEscaper;
|
||||||
|
import org.apache.commons.lang3.text.translate.LookupTranslator;
|
||||||
|
import org.apache.commons.lang3.text.translate.NumericEntityEscaper;
|
||||||
|
import org.apache.commons.lang3.text.translate.NumericEntityUnescaper;
|
||||||
|
import org.apache.commons.lang3.text.translate.OctalUnescaper;
|
||||||
|
import org.apache.commons.lang3.text.translate.UnicodeUnescaper;
|
||||||
|
import org.apache.commons.lang3.text.translate.UnicodeUnpairedSurrogateRemover;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Escapes and unescapes {@code String}s for
|
||||||
|
* Java, Java Script, HTML and XML.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
* @since 2.0
|
||||||
|
* @deprecated as of 3.6, use commons-text
|
||||||
|
* <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/StringEscapeUtils.html">
|
||||||
|
* StringEscapeUtils</a> instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class StringEscapeUtils {
|
||||||
|
|
||||||
|
/* ESCAPE TRANSLATORS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping Java.
|
||||||
|
*
|
||||||
|
* While {@link #escapeJava(String)} is the expected method of use, this
|
||||||
|
* object allows the Java escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_JAVA =
|
||||||
|
new LookupTranslator(
|
||||||
|
new String[][] {
|
||||||
|
{"\"", "\\\""},
|
||||||
|
{"\\", "\\\\"},
|
||||||
|
}).with(
|
||||||
|
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE())
|
||||||
|
).with(
|
||||||
|
JavaUnicodeEscaper.outsideOf(32, 0x7f)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping EcmaScript/JavaScript.
|
||||||
|
*
|
||||||
|
* While {@link #escapeEcmaScript(String)} is the expected method of use, this
|
||||||
|
* object allows the EcmaScript escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_ECMASCRIPT =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(
|
||||||
|
new String[][] {
|
||||||
|
{"'", "\\'"},
|
||||||
|
{"\"", "\\\""},
|
||||||
|
{"\\", "\\\\"},
|
||||||
|
{"/", "\\/"}
|
||||||
|
}),
|
||||||
|
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()),
|
||||||
|
JavaUnicodeEscaper.outsideOf(32, 0x7f)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping Json.
|
||||||
|
*
|
||||||
|
* While {@link #escapeJson(String)} is the expected method of use, this
|
||||||
|
* object allows the Json escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_JSON =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(
|
||||||
|
new String[][] {
|
||||||
|
{"\"", "\\\""},
|
||||||
|
{"\\", "\\\\"},
|
||||||
|
{"/", "\\/"}
|
||||||
|
}),
|
||||||
|
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()),
|
||||||
|
JavaUnicodeEscaper.outsideOf(32, 0x7f)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping XML.
|
||||||
|
*
|
||||||
|
* While {@link #escapeXml(String)} is the expected method of use, this
|
||||||
|
* object allows the XML escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
* @deprecated use {@link #ESCAPE_XML10} or {@link #ESCAPE_XML11} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final CharSequenceTranslator ESCAPE_XML =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.APOS_ESCAPE())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping XML 1.0.
|
||||||
|
*
|
||||||
|
* While {@link #escapeXml10(String)} is the expected method of use, this
|
||||||
|
* object allows the XML escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_XML10 =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.APOS_ESCAPE()),
|
||||||
|
new LookupTranslator(
|
||||||
|
new String[][] {
|
||||||
|
{ "\u0000", StringUtils.EMPTY },
|
||||||
|
{ "\u0001", StringUtils.EMPTY },
|
||||||
|
{ "\u0002", StringUtils.EMPTY },
|
||||||
|
{ "\u0003", StringUtils.EMPTY },
|
||||||
|
{ "\u0004", StringUtils.EMPTY },
|
||||||
|
{ "\u0005", StringUtils.EMPTY },
|
||||||
|
{ "\u0006", StringUtils.EMPTY },
|
||||||
|
{ "\u0007", StringUtils.EMPTY },
|
||||||
|
{ "\u0008", StringUtils.EMPTY },
|
||||||
|
{ "\u000b", StringUtils.EMPTY },
|
||||||
|
{ "\u000c", StringUtils.EMPTY },
|
||||||
|
{ "\u000e", StringUtils.EMPTY },
|
||||||
|
{ "\u000f", StringUtils.EMPTY },
|
||||||
|
{ "\u0010", StringUtils.EMPTY },
|
||||||
|
{ "\u0011", StringUtils.EMPTY },
|
||||||
|
{ "\u0012", StringUtils.EMPTY },
|
||||||
|
{ "\u0013", StringUtils.EMPTY },
|
||||||
|
{ "\u0014", StringUtils.EMPTY },
|
||||||
|
{ "\u0015", StringUtils.EMPTY },
|
||||||
|
{ "\u0016", StringUtils.EMPTY },
|
||||||
|
{ "\u0017", StringUtils.EMPTY },
|
||||||
|
{ "\u0018", StringUtils.EMPTY },
|
||||||
|
{ "\u0019", StringUtils.EMPTY },
|
||||||
|
{ "\u001a", StringUtils.EMPTY },
|
||||||
|
{ "\u001b", StringUtils.EMPTY },
|
||||||
|
{ "\u001c", StringUtils.EMPTY },
|
||||||
|
{ "\u001d", StringUtils.EMPTY },
|
||||||
|
{ "\u001e", StringUtils.EMPTY },
|
||||||
|
{ "\u001f", StringUtils.EMPTY },
|
||||||
|
{ "\ufffe", StringUtils.EMPTY },
|
||||||
|
{ "\uffff", StringUtils.EMPTY }
|
||||||
|
}),
|
||||||
|
NumericEntityEscaper.between(0x7f, 0x84),
|
||||||
|
NumericEntityEscaper.between(0x86, 0x9f),
|
||||||
|
new UnicodeUnpairedSurrogateRemover()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping XML 1.1.
|
||||||
|
*
|
||||||
|
* While {@link #escapeXml11(String)} is the expected method of use, this
|
||||||
|
* object allows the XML escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_XML11 =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.APOS_ESCAPE()),
|
||||||
|
new LookupTranslator(
|
||||||
|
new String[][] {
|
||||||
|
{ "\u0000", StringUtils.EMPTY },
|
||||||
|
{ "\u000b", "" },
|
||||||
|
{ "\u000c", "" },
|
||||||
|
{ "\ufffe", StringUtils.EMPTY },
|
||||||
|
{ "\uffff", StringUtils.EMPTY }
|
||||||
|
}),
|
||||||
|
NumericEntityEscaper.between(0x1, 0x8),
|
||||||
|
NumericEntityEscaper.between(0xe, 0x1f),
|
||||||
|
NumericEntityEscaper.between(0x7f, 0x84),
|
||||||
|
NumericEntityEscaper.between(0x86, 0x9f),
|
||||||
|
new UnicodeUnpairedSurrogateRemover()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping HTML version 3.0.
|
||||||
|
*
|
||||||
|
* While {@link #escapeHtml3(String)} is the expected method of use, this
|
||||||
|
* object allows the HTML escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_HTML3 =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping HTML version 4.0.
|
||||||
|
*
|
||||||
|
* While {@link #escapeHtml4(String)} is the expected method of use, this
|
||||||
|
* object allows the HTML escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_HTML4 =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for escaping individual Comma Separated Values.
|
||||||
|
*
|
||||||
|
* While {@link #escapeCsv(String)} is the expected method of use, this
|
||||||
|
* object allows the CSV escaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper();
|
||||||
|
|
||||||
|
// TODO: Create a parent class - 'SinglePassTranslator' ?
|
||||||
|
// It would handle the index checking + length returning,
|
||||||
|
// and could also have an optimization check method.
|
||||||
|
static class CsvEscaper extends CharSequenceTranslator {
|
||||||
|
|
||||||
|
private static final char CSV_DELIMITER = ',';
|
||||||
|
private static final char CSV_QUOTE = '"';
|
||||||
|
private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
|
||||||
|
private static final char[] CSV_SEARCH_CHARS = { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF };
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
|
||||||
|
|
||||||
|
if (index != 0) {
|
||||||
|
throw new IllegalStateException("CsvEscaper should never reach the [1] index");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) {
|
||||||
|
out.write(input.toString());
|
||||||
|
} else {
|
||||||
|
out.write(CSV_QUOTE);
|
||||||
|
out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR));
|
||||||
|
out.write(CSV_QUOTE);
|
||||||
|
}
|
||||||
|
return Character.codePointCount(input, 0, input.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UNESCAPE TRANSLATORS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped Java.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeJava(String)} is the expected method of use, this
|
||||||
|
* object allows the Java unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
// TODO: throw "illegal character: \92" as an Exception if a \ on the end of the Java (as per the compiler)?
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_JAVA =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new OctalUnescaper(), // .between('\1', '\377'),
|
||||||
|
new UnicodeUnescaper(),
|
||||||
|
new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()),
|
||||||
|
new LookupTranslator(
|
||||||
|
new String[][] {
|
||||||
|
{"\\\\", "\\"},
|
||||||
|
{"\\\"", "\""},
|
||||||
|
{"\\'", "'"},
|
||||||
|
{"\\", ""}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped EcmaScript.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeEcmaScript(String)} is the expected method of use, this
|
||||||
|
* object allows the EcmaScript unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped Json.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeJson(String)} is the expected method of use, this
|
||||||
|
* object allows the Json unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped HTML 3.0.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeHtml3(String)} is the expected method of use, this
|
||||||
|
* object allows the HTML unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_HTML3 =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
|
||||||
|
new NumericEntityUnescaper()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped HTML 4.0.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeHtml4(String)} is the expected method of use, this
|
||||||
|
* object allows the HTML unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_HTML4 =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()),
|
||||||
|
new NumericEntityUnescaper()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped XML.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeXml(String)} is the expected method of use, this
|
||||||
|
* object allows the XML unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_XML =
|
||||||
|
new AggregateTranslator(
|
||||||
|
new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
|
||||||
|
new LookupTranslator(EntityArrays.APOS_UNESCAPE()),
|
||||||
|
new NumericEntityUnescaper()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator object for unescaping escaped Comma Separated Value entries.
|
||||||
|
*
|
||||||
|
* While {@link #unescapeCsv(String)} is the expected method of use, this
|
||||||
|
* object allows the CSV unescaping functionality to be used
|
||||||
|
* as the foundation for a custom translator.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper();
|
||||||
|
|
||||||
|
static class CsvUnescaper extends CharSequenceTranslator {
|
||||||
|
|
||||||
|
private static final char CSV_DELIMITER = ',';
|
||||||
|
private static final char CSV_QUOTE = '"';
|
||||||
|
private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
|
||||||
|
private static final char[] CSV_SEARCH_CHARS = {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
|
||||||
|
|
||||||
|
if (index != 0) {
|
||||||
|
throw new IllegalStateException("CsvUnescaper should never reach the [1] index");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) {
|
||||||
|
out.write(input.toString());
|
||||||
|
return Character.codePointCount(input, 0, input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip quotes
|
||||||
|
final String quoteless = input.subSequence(1, input.length() - 1).toString();
|
||||||
|
|
||||||
|
if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) {
|
||||||
|
// deal with escaped quotes; ie) ""
|
||||||
|
out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR));
|
||||||
|
} else {
|
||||||
|
out.write(input.toString());
|
||||||
|
}
|
||||||
|
return Character.codePointCount(input, 0, input.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code StringEscapeUtils} instances should NOT be constructed in
|
||||||
|
* standard programming.</p>
|
||||||
|
*
|
||||||
|
* <p>Instead, the class should be used as:</p>
|
||||||
|
* <pre>StringEscapeUtils.escapeJava("foo");</pre>
|
||||||
|
*
|
||||||
|
* <p>This constructor is public to permit tools that require a JavaBean
|
||||||
|
* instance to operate.</p>
|
||||||
|
*/
|
||||||
|
public StringEscapeUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java and JavaScript
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using Java String rules.</p>
|
||||||
|
*
|
||||||
|
* <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
|
||||||
|
*
|
||||||
|
* <p>So a tab becomes the characters {@code '\\'} and
|
||||||
|
* {@code 't'}.</p>
|
||||||
|
*
|
||||||
|
* <p>The only difference between Java strings and JavaScript strings
|
||||||
|
* is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
|
||||||
|
*
|
||||||
|
* <p>Example:</p>
|
||||||
|
* <pre>
|
||||||
|
* input string: He didn't say, "Stop!"
|
||||||
|
* output string: He didn't say, \"Stop!\"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param input String to escape values in, may be null
|
||||||
|
* @return String with escaped values, {@code null} if null string input
|
||||||
|
*/
|
||||||
|
public static final String escapeJava(final String input) {
|
||||||
|
return ESCAPE_JAVA.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using EcmaScript String rules.</p>
|
||||||
|
* <p>Escapes any values it finds into their EcmaScript String form.
|
||||||
|
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
|
||||||
|
*
|
||||||
|
* <p>So a tab becomes the characters {@code '\\'} and
|
||||||
|
* {@code 't'}.</p>
|
||||||
|
*
|
||||||
|
* <p>The only difference between Java strings and EcmaScript strings
|
||||||
|
* is that in EcmaScript, a single quote and forward-slash (/) are escaped.</p>
|
||||||
|
*
|
||||||
|
* <p>Note that EcmaScript is best known by the JavaScript and ActionScript dialects. </p>
|
||||||
|
*
|
||||||
|
* <p>Example:</p>
|
||||||
|
* <pre>
|
||||||
|
* input string: He didn't say, "Stop!"
|
||||||
|
* output string: He didn\'t say, \"Stop!\"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param input String to escape values in, may be null
|
||||||
|
* @return String with escaped values, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final String escapeEcmaScript(final String input) {
|
||||||
|
return ESCAPE_ECMASCRIPT.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using Json String rules.</p>
|
||||||
|
* <p>Escapes any values it finds into their Json String form.
|
||||||
|
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
|
||||||
|
*
|
||||||
|
* <p>So a tab becomes the characters {@code '\\'} and
|
||||||
|
* {@code 't'}.</p>
|
||||||
|
*
|
||||||
|
* <p>The only difference between Java strings and Json strings
|
||||||
|
* is that in Json, forward-slash (/) is escaped.</p>
|
||||||
|
*
|
||||||
|
* <p>See http://www.ietf.org/rfc/rfc4627.txt for further details. </p>
|
||||||
|
*
|
||||||
|
* <p>Example:</p>
|
||||||
|
* <pre>
|
||||||
|
* input string: He didn't say, "Stop!"
|
||||||
|
* output string: He didn't say, \"Stop!\"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param input String to escape values in, may be null
|
||||||
|
* @return String with escaped values, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static final String escapeJson(final String input) {
|
||||||
|
return ESCAPE_JSON.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Unescapes any Java literals found in the {@code String}.
|
||||||
|
* For example, it will turn a sequence of {@code '\'} and
|
||||||
|
* {@code 'n'} into a newline character, unless the {@code '\'}
|
||||||
|
* is preceded by another {@code '\'}.</p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to unescape, may be null
|
||||||
|
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||||
|
*/
|
||||||
|
public static final String unescapeJava(final String input) {
|
||||||
|
return UNESCAPE_JAVA.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Unescapes any EcmaScript literals found in the {@code String}.</p>
|
||||||
|
*
|
||||||
|
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
|
||||||
|
* into a newline character, unless the {@code '\'} is preceded by another
|
||||||
|
* {@code '\'}.</p>
|
||||||
|
*
|
||||||
|
* @see #unescapeJava(String)
|
||||||
|
* @param input the {@code String} to unescape, may be null
|
||||||
|
* @return A new unescaped {@code String}, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final String unescapeEcmaScript(final String input) {
|
||||||
|
return UNESCAPE_ECMASCRIPT.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Unescapes any Json literals found in the {@code String}.</p>
|
||||||
|
*
|
||||||
|
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
|
||||||
|
* into a newline character, unless the {@code '\'} is preceded by another
|
||||||
|
* {@code '\'}.</p>
|
||||||
|
*
|
||||||
|
* @see #unescapeJava(String)
|
||||||
|
* @param input the {@code String} to unescape, may be null
|
||||||
|
* @return A new unescaped {@code String}, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public static final String unescapeJson(final String input) {
|
||||||
|
return UNESCAPE_JSON.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML and XML
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using HTML entities.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For example:
|
||||||
|
* </p>
|
||||||
|
* <p>{@code "bread" & "butter"}</p>
|
||||||
|
* becomes:
|
||||||
|
* <p>
|
||||||
|
* {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Supports all known HTML 4.0 entities, including funky accents.
|
||||||
|
* Note that the commonly used apostrophe escape character (&apos;)
|
||||||
|
* is not a legal entity and so is not supported). </p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to escape, may be null
|
||||||
|
* @return a new escaped {@code String}, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @see <a href="http://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO Entities</a>
|
||||||
|
* @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character Entities for ISO Latin-1</a>
|
||||||
|
* @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0 Character entity references</a>
|
||||||
|
* @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01 Character References</a>
|
||||||
|
* @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML 4.01 Code positions</a>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final String escapeHtml4(final String input) {
|
||||||
|
return ESCAPE_HTML4.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using HTML entities.</p>
|
||||||
|
* <p>Supports only the HTML 3.0 entities. </p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to escape, may be null
|
||||||
|
* @return a new escaped {@code String}, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final String escapeHtml3(final String input) {
|
||||||
|
return ESCAPE_HTML3.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Unescapes a string containing entity escapes to a string
|
||||||
|
* containing the actual Unicode characters corresponding to the
|
||||||
|
* escapes. Supports HTML 4.0 entities.</p>
|
||||||
|
*
|
||||||
|
* <p>For example, the string {@code "<Français>"}
|
||||||
|
* will become {@code "<Français>"}</p>
|
||||||
|
*
|
||||||
|
* <p>If an entity is unrecognized, it is left alone, and inserted
|
||||||
|
* verbatim into the result string. e.g. {@code ">&zzzz;x"} will
|
||||||
|
* become {@code ">&zzzz;x"}.</p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to unescape, may be null
|
||||||
|
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final String unescapeHtml4(final String input) {
|
||||||
|
return UNESCAPE_HTML4.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Unescapes a string containing entity escapes to a string
|
||||||
|
* containing the actual Unicode characters corresponding to the
|
||||||
|
* escapes. Supports only HTML 3.0 entities.</p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to unescape, may be null
|
||||||
|
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public static final String unescapeHtml3(final String input) {
|
||||||
|
return UNESCAPE_HTML3.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using XML entities.</p>
|
||||||
|
*
|
||||||
|
* <p>For example: {@code "bread" & "butter"} =>
|
||||||
|
* {@code "bread" & "butter"}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
|
||||||
|
* Does not support DTDs or external entities.</p>
|
||||||
|
*
|
||||||
|
* <p>Note that Unicode characters greater than 0x7f are as of 3.0, no longer
|
||||||
|
* escaped. If you still wish this functionality, you can achieve it
|
||||||
|
* via the following:
|
||||||
|
* {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );}</p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to escape, may be null
|
||||||
|
* @return a new escaped {@code String}, {@code null} if null string input
|
||||||
|
* @see #unescapeXml(java.lang.String)
|
||||||
|
* @deprecated use {@link #escapeXml10(java.lang.String)} or {@link #escapeXml11(java.lang.String)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final String escapeXml(final String input) {
|
||||||
|
return ESCAPE_XML.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using XML entities.</p>
|
||||||
|
*
|
||||||
|
* <p>For example: {@code "bread" & "butter"} =>
|
||||||
|
* {@code "bread" & "butter"}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Note that XML 1.0 is a text-only format: it cannot represent control
|
||||||
|
* characters or unpaired Unicode surrogate codepoints, even after escaping.
|
||||||
|
* {@code escapeXml10} will remove characters that do not fit in the
|
||||||
|
* following ranges:</p>
|
||||||
|
*
|
||||||
|
* <p>{@code #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
|
||||||
|
*
|
||||||
|
* <p>Though not strictly necessary, {@code escapeXml10} will escape
|
||||||
|
* characters in the following ranges:</p>
|
||||||
|
*
|
||||||
|
* <p>{@code [#x7F-#x84] | [#x86-#x9F]}</p>
|
||||||
|
*
|
||||||
|
* <p>The returned string can be inserted into a valid XML 1.0 or XML 1.1
|
||||||
|
* document. If you want to allow more non-text characters in an XML 1.1
|
||||||
|
* document, use {@link #escapeXml11(String)}.</p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to escape, may be null
|
||||||
|
* @return a new escaped {@code String}, {@code null} if null string input
|
||||||
|
* @see #unescapeXml(java.lang.String)
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public static String escapeXml10(final String input) {
|
||||||
|
return ESCAPE_XML10.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Escapes the characters in a {@code String} using XML entities.</p>
|
||||||
|
*
|
||||||
|
* <p>For example: {@code "bread" & "butter"} =>
|
||||||
|
* {@code "bread" & "butter"}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>XML 1.1 can represent certain control characters, but it cannot represent
|
||||||
|
* the null byte or unpaired Unicode surrogate codepoints, even after escaping.
|
||||||
|
* {@code escapeXml11} will remove characters that do not fit in the following
|
||||||
|
* ranges:</p>
|
||||||
|
*
|
||||||
|
* <p>{@code [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
|
||||||
|
*
|
||||||
|
* <p>{@code escapeXml11} will escape characters in the following ranges:</p>
|
||||||
|
*
|
||||||
|
* <p>{@code [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]}</p>
|
||||||
|
*
|
||||||
|
* <p>The returned string can be inserted into a valid XML 1.1 document. Do not
|
||||||
|
* use it for XML 1.0 documents.</p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to escape, may be null
|
||||||
|
* @return a new escaped {@code String}, {@code null} if null string input
|
||||||
|
* @see #unescapeXml(java.lang.String)
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public static String escapeXml11(final String input) {
|
||||||
|
return ESCAPE_XML11.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* <p>Unescapes a string containing XML entity escapes to a string
|
||||||
|
* containing the actual Unicode characters corresponding to the
|
||||||
|
* escapes.</p>
|
||||||
|
*
|
||||||
|
* <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
|
||||||
|
* Does not support DTDs or external entities.</p>
|
||||||
|
*
|
||||||
|
* <p>Note that numerical \\u Unicode codes are unescaped to their respective
|
||||||
|
* Unicode characters. This may change in future releases. </p>
|
||||||
|
*
|
||||||
|
* @param input the {@code String} to unescape, may be null
|
||||||
|
* @return a new unescaped {@code String}, {@code null} if null string input
|
||||||
|
* @see #escapeXml(String)
|
||||||
|
* @see #escapeXml10(String)
|
||||||
|
* @see #escapeXml11(String)
|
||||||
|
*/
|
||||||
|
public static final String unescapeXml(final String input) {
|
||||||
|
return UNESCAPE_XML.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns a {@code String} value for a CSV column enclosed in double quotes,
|
||||||
|
* if required.</p>
|
||||||
|
*
|
||||||
|
* <p>If the value contains a comma, newline or double quote, then the
|
||||||
|
* String value is returned enclosed in double quotes.</p>
|
||||||
|
*
|
||||||
|
* <p>Any double quote characters in the value are escaped with another double quote.</p>
|
||||||
|
*
|
||||||
|
* <p>If the value does not contain a comma, newline or double quote, then the
|
||||||
|
* String value is returned unchanged.</p>
|
||||||
|
*
|
||||||
|
* see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
|
||||||
|
* <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||||||
|
*
|
||||||
|
* @param input the input CSV column String, may be null
|
||||||
|
* @return the input String, enclosed in double quotes if the value contains a comma,
|
||||||
|
* newline or double quote, {@code null} if null string input
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
public static final String escapeCsv(final String input) {
|
||||||
|
return ESCAPE_CSV.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns a {@code String} value for an unescaped CSV column. </p>
|
||||||
|
*
|
||||||
|
* <p>If the value is enclosed in double quotes, and contains a comma, newline
|
||||||
|
* or double quote, then quotes are removed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Any double quote escaped characters (a pair of double quotes) are unescaped
|
||||||
|
* to just one double quote. </p>
|
||||||
|
*
|
||||||
|
* <p>If the value is not enclosed in double quotes, or is and does not contain a
|
||||||
|
* comma, newline or double quote, then the String value is returned unchanged.</p>
|
||||||
|
*
|
||||||
|
* see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
|
||||||
|
* <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||||||
|
*
|
||||||
|
* @param input the input CSV column String, may be null
|
||||||
|
* @return the input String, with enclosing double quotes removed and embedded double
|
||||||
|
* quotes unescaped, {@code null} if null string input
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
public static final String unescapeCsv(final String input) {
|
||||||
|
return UNESCAPE_CSV.translate(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9650
after/src/main/java/org/apache/commons/lang3/StringUtils.java
Normal file
9650
after/src/main/java/org/apache/commons/lang3/StringUtils.java
Normal file
File diff suppressed because it is too large
Load diff
1945
after/src/main/java/org/apache/commons/lang3/SystemUtils.java
Normal file
1945
after/src/main/java/org/apache/commons/lang3/SystemUtils.java
Normal file
File diff suppressed because it is too large
Load diff
469
after/src/main/java/org/apache/commons/lang3/ThreadUtils.java
Normal file
469
after/src/main/java/org/apache/commons/lang3/ThreadUtils.java
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.time.DurationUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* #ThreadSafe#
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see java.lang.Thread
|
||||||
|
* @see java.lang.ThreadGroup
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public class ThreadUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A predicate implementation which always returns true.
|
||||||
|
*/
|
||||||
|
private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate {
|
||||||
|
|
||||||
|
private AlwaysTruePredicate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(final Thread thread) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(final ThreadGroup threadGroup) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A predicate implementation which matches a thread or threadgroup name.
|
||||||
|
*/
|
||||||
|
public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate constructor
|
||||||
|
*
|
||||||
|
* @param name thread or threadgroup name
|
||||||
|
* @throws IllegalArgumentException if the name is {@code null}
|
||||||
|
*/
|
||||||
|
public NamePredicate(final String name) {
|
||||||
|
Validate.notNull(name, "name");
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(final Thread thread) {
|
||||||
|
return thread != null && thread.getName().equals(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(final ThreadGroup threadGroup) {
|
||||||
|
return threadGroup != null && threadGroup.getName().equals(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A predicate for selecting threadgroups.
|
||||||
|
*/
|
||||||
|
// When breaking BC, replace this with Predicate<ThreadGroup>
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThreadGroupPredicate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates this predicate on the given threadgroup.
|
||||||
|
* @param threadGroup the threadgroup
|
||||||
|
* @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
|
||||||
|
*/
|
||||||
|
boolean test(ThreadGroup threadGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A predicate implementation which matches a thread id.
|
||||||
|
*/
|
||||||
|
public static class ThreadIdPredicate implements ThreadPredicate {
|
||||||
|
|
||||||
|
private final long threadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate constructor
|
||||||
|
*
|
||||||
|
* @param threadId the threadId to match
|
||||||
|
* @throws IllegalArgumentException if the threadId is zero or negative
|
||||||
|
*/
|
||||||
|
public ThreadIdPredicate(final long threadId) {
|
||||||
|
if (threadId <= 0) {
|
||||||
|
throw new IllegalArgumentException("The thread id must be greater than zero");
|
||||||
|
}
|
||||||
|
this.threadId = threadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(final Thread thread) {
|
||||||
|
return thread != null && thread.getId() == threadId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A predicate for selecting threads.
|
||||||
|
*/
|
||||||
|
// When breaking BC, replace this with Predicate<Thread>
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThreadPredicate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates this predicate on the given thread.
|
||||||
|
* @param thread the thread
|
||||||
|
* @return {@code true} if the thread matches the predicate, otherwise {@code false}
|
||||||
|
*/
|
||||||
|
boolean test(Thread thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate which always returns true.
|
||||||
|
*/
|
||||||
|
public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the active thread with the specified id.
|
||||||
|
*
|
||||||
|
* @param threadId The thread id
|
||||||
|
* @return The thread with the specified id or {@code null} if no such thread exists
|
||||||
|
* @throws IllegalArgumentException if the specified id is zero or negative
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Thread findThreadById(final long threadId) {
|
||||||
|
final Collection<Thread> result = findThreads(new ThreadIdPredicate(threadId));
|
||||||
|
return result.isEmpty() ? null : result.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the active thread with the specified id if it belongs to a thread group with the specified group name.
|
||||||
|
*
|
||||||
|
* @param threadId The thread id
|
||||||
|
* @param threadGroupName The thread group name
|
||||||
|
* @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id.
|
||||||
|
* {@code null} is returned if no such thread exists
|
||||||
|
* @throws IllegalArgumentException if the specified id is zero or negative or the group name is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Thread findThreadById(final long threadId, final String threadGroupName) {
|
||||||
|
Validate.notNull(threadGroupName, "threadGroupName");
|
||||||
|
final Thread thread = findThreadById(threadId);
|
||||||
|
if (thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the active thread with the specified id if it belongs to the specified thread group.
|
||||||
|
*
|
||||||
|
* @param threadId The thread id
|
||||||
|
* @param threadGroup The thread group
|
||||||
|
* @return The thread which belongs to a specified thread group and the thread's id match the specified id.
|
||||||
|
* {@code null} is returned if no such thread exists
|
||||||
|
* @throws IllegalArgumentException if the specified id is zero or negative or the group is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) {
|
||||||
|
Validate.notNull(threadGroup, "threadGroup");
|
||||||
|
final Thread thread = findThreadById(threadId);
|
||||||
|
if (thread != null && threadGroup.equals(thread.getThreadGroup())) {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
|
||||||
|
*
|
||||||
|
* @param group the thread group
|
||||||
|
* @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
|
||||||
|
* @param predicate the predicate
|
||||||
|
* @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
|
||||||
|
* @throws IllegalArgumentException if the given group or predicate is null
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate) {
|
||||||
|
Validate.notNull(group, "group");
|
||||||
|
Validate.notNull(predicate, "predicate");
|
||||||
|
|
||||||
|
int count = group.activeGroupCount();
|
||||||
|
ThreadGroup[] threadGroups;
|
||||||
|
do {
|
||||||
|
threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size
|
||||||
|
count = group.enumerate(threadGroups, recurse);
|
||||||
|
//return value of enumerate() must be strictly less than the array size according to javadoc
|
||||||
|
} while (count >= threadGroups.length);
|
||||||
|
|
||||||
|
final List<ThreadGroup> result = new ArrayList<>(count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (predicate.test(threadGroups[i])) {
|
||||||
|
result.add(threadGroups[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableCollection(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all active threadgroups which match the given predicate.
|
||||||
|
*
|
||||||
|
* @param predicate the predicate
|
||||||
|
* @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
|
||||||
|
* @throws IllegalArgumentException if the predicate is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate) {
|
||||||
|
return findThreadGroups(getSystemThreadGroup(), true, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds active thread groups with the specified group name.
|
||||||
|
*
|
||||||
|
* @param threadGroupName The thread group name
|
||||||
|
* @return the thread groups with the specified group name or an empty collection if no such thread group exists. The collection returned is always unmodifiable.
|
||||||
|
* @throws IllegalArgumentException if group name is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<ThreadGroup> findThreadGroupsByName(final String threadGroupName) {
|
||||||
|
return findThreadGroups(new NamePredicate(threadGroupName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
|
||||||
|
*
|
||||||
|
* @param group the thread group
|
||||||
|
* @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
|
||||||
|
* @param predicate the predicate
|
||||||
|
* @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
|
||||||
|
* @throws IllegalArgumentException if the given group or predicate is null
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
|
||||||
|
Validate.notNull(group, "The group must not be null");
|
||||||
|
Validate.notNull(predicate, "The predicate must not be null");
|
||||||
|
|
||||||
|
int count = group.activeCount();
|
||||||
|
Thread[] threads;
|
||||||
|
do {
|
||||||
|
threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size
|
||||||
|
count = group.enumerate(threads, recurse);
|
||||||
|
//return value of enumerate() must be strictly less than the array size according to javadoc
|
||||||
|
} while (count >= threads.length);
|
||||||
|
|
||||||
|
final List<Thread> result = new ArrayList<>(count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (predicate.test(threads[i])) {
|
||||||
|
result.add(threads[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableCollection(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all active threads which match the given predicate.
|
||||||
|
*
|
||||||
|
* @param predicate the predicate
|
||||||
|
* @return An unmodifiable {@code Collection} of active threads matching the given predicate
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the predicate is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<Thread> findThreads(final ThreadPredicate predicate) {
|
||||||
|
return findThreads(getSystemThreadGroup(), true, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds active threads with the specified name.
|
||||||
|
*
|
||||||
|
* @param threadName The thread name
|
||||||
|
* @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable.
|
||||||
|
* @throws IllegalArgumentException if the specified name is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<Thread> findThreadsByName(final String threadName) {
|
||||||
|
return findThreads(new NamePredicate(threadName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds active threads with the specified name if they belong to a thread group with the specified group name.
|
||||||
|
*
|
||||||
|
* @param threadName The thread name
|
||||||
|
* @param threadGroupName The thread group name
|
||||||
|
* @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name,
|
||||||
|
* An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
|
||||||
|
* @throws IllegalArgumentException if the specified thread name or group name is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) {
|
||||||
|
Validate.notNull(threadName, "threadName");
|
||||||
|
Validate.notNull(threadGroupName, "threadGroupName");
|
||||||
|
|
||||||
|
final Collection<ThreadGroup> threadGroups = findThreadGroups(new NamePredicate(threadGroupName));
|
||||||
|
|
||||||
|
if (threadGroups.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Collection<Thread> result = new ArrayList<>();
|
||||||
|
final NamePredicate threadNamePredicate = new NamePredicate(threadName);
|
||||||
|
for (final ThreadGroup group : threadGroups) {
|
||||||
|
result.addAll(findThreads(group, false, threadNamePredicate));
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableCollection(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds active threads with the specified name if they belong to a specified thread group.
|
||||||
|
*
|
||||||
|
* @param threadName The thread name
|
||||||
|
* @param threadGroup The thread group
|
||||||
|
* @return The threads which belongs to a thread group and the thread's name match the specified name,
|
||||||
|
* An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
|
||||||
|
* @throws IllegalArgumentException if the specified thread name or group is null
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) {
|
||||||
|
return findThreads(threadGroup, false, new NamePredicate(threadName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed).
|
||||||
|
*
|
||||||
|
* @return all thread groups excluding the system thread group. The collection returned is always unmodifiable.
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<ThreadGroup> getAllThreadGroups() {
|
||||||
|
return findThreadGroups(ALWAYS_TRUE_PREDICATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all active threads (A thread is active if it has been started and has not yet died).
|
||||||
|
*
|
||||||
|
* @return all active threads. The collection returned is always unmodifiable.
|
||||||
|
* @throws SecurityException
|
||||||
|
* if the current thread cannot access the system thread group
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static Collection<Thread> getAllThreads() {
|
||||||
|
return findThreads(ALWAYS_TRUE_PREDICATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the system thread group (sometimes also referred as "root thread group").
|
||||||
|
*
|
||||||
|
* @return the system thread group
|
||||||
|
* @throws SecurityException if the current thread cannot modify
|
||||||
|
* thread groups from this thread's thread group up to the system thread group
|
||||||
|
*/
|
||||||
|
public static ThreadGroup getSystemThreadGroup() {
|
||||||
|
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||||
|
while (threadGroup.getParent() != null) {
|
||||||
|
threadGroup = threadGroup.getParent();
|
||||||
|
}
|
||||||
|
return threadGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the given thread to die for the given duration. Implemented using {@link Thread#join(long, int)}.
|
||||||
|
*
|
||||||
|
* @param thread The thread to join.
|
||||||
|
* @param duration How long to wait.
|
||||||
|
* @throws InterruptedException if any thread has interrupted the current thread.
|
||||||
|
* @see Thread#join(long, int)
|
||||||
|
* @since 3.12.0
|
||||||
|
*/
|
||||||
|
public static void join(final Thread thread, final Duration duration) throws InterruptedException {
|
||||||
|
DurationUtils.accept(thread::join, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleeps the current thread for the given duration. Implemented using {@link Thread#sleep(long, int)}.
|
||||||
|
*
|
||||||
|
* @param duration How long to sleep.
|
||||||
|
* @throws InterruptedException if any thread has interrupted the current thread.
|
||||||
|
* @see Thread#sleep(long, int)
|
||||||
|
* @since 3.12.0
|
||||||
|
*/
|
||||||
|
public static void sleep(final Duration duration) throws InterruptedException {
|
||||||
|
DurationUtils.accept(Thread::sleep, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
|
||||||
|
* {@code ThreadUtils.getAllThreads()}
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This constructor is public to permit tools that require a JavaBean instance to operate.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public ThreadUtils() {
|
||||||
|
}
|
||||||
|
}
|
1329
after/src/main/java/org/apache/commons/lang3/Validate.java
Normal file
1329
after/src/main/java/org/apache/commons/lang3/Validate.java
Normal file
File diff suppressed because it is too large
Load diff
191
after/src/main/java/org/apache/commons/lang3/arch/Processor.java
Normal file
191
after/src/main/java/org/apache/commons/lang3/arch/Processor.java
Normal 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,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apple–IBM–Motorola PowerPC architecture.
|
||||||
|
*/
|
||||||
|
PPC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unknown architecture.
|
||||||
|
*/
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Arch arch;
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link Processor} object with the given
|
||||||
|
* parameters.
|
||||||
|
*
|
||||||
|
* @param arch The processor architecture.
|
||||||
|
* @param type The processor type.
|
||||||
|
*/
|
||||||
|
public Processor(final Arch arch, final Type type) {
|
||||||
|
this.arch = arch;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the processor architecture as an {@link Arch} enum.
|
||||||
|
* The processor architecture defines, if the processor has
|
||||||
|
* a 32 or 64 bit architecture.
|
||||||
|
*
|
||||||
|
* @return A {@link Arch} enum.
|
||||||
|
*/
|
||||||
|
public Arch getArch() {
|
||||||
|
return arch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the processor type as {@link Type} enum.
|
||||||
|
* The processor type defines, if the processor is for example
|
||||||
|
* a x86 or PPA.
|
||||||
|
*
|
||||||
|
* @return A {@link Type} enum.
|
||||||
|
*/
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@link Processor} is 32 bit.
|
||||||
|
*
|
||||||
|
* @return {@code true}, if {@link Processor} is {@link Arch#BIT_32}, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean is32Bit() {
|
||||||
|
return Arch.BIT_32 == arch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@link Processor} is 64 bit.
|
||||||
|
*
|
||||||
|
* @return {@code true}, if {@link Processor} is {@link Arch#BIT_64}, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean is64Bit() {
|
||||||
|
return Arch.BIT_64 == arch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@link Processor} is type of x86.
|
||||||
|
*
|
||||||
|
* @return {@code true}, if {@link Processor} is {@link Type#X86}, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean isX86() {
|
||||||
|
return Type.X86 == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@link Processor} is type of Intel Itanium.
|
||||||
|
*
|
||||||
|
* @return {@code true}. if {@link Processor} is {@link Type#IA_64}, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean isIA64() {
|
||||||
|
return Type.IA_64 == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@link Processor} is type of Power PC.
|
||||||
|
*
|
||||||
|
* @return {@code true}. if {@link Processor} is {@link Type#PPC}, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean isPPC() {
|
||||||
|
return Type.PPC == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Provides classes to work with the values of the os.arch system property.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.arch;
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The Builder interface is designed to designate a class as a <em>builder</em>
|
||||||
|
* object in the Builder design pattern. Builders are capable of creating and
|
||||||
|
* configuring objects or results that normally take multiple steps to construct
|
||||||
|
* or are very complex to derive.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The builder interface defines a single method, {@link #build()}, that
|
||||||
|
* classes must implement. The result of this method should be the final
|
||||||
|
* configured object or result after all building operations are performed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It is a recommended practice that the methods supplied to configure the
|
||||||
|
* object or result being built return a reference to {@code this} so that
|
||||||
|
* method calls can be chained together.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Example Builder:
|
||||||
|
* <pre><code>
|
||||||
|
* class FontBuilder implements Builder<Font> {
|
||||||
|
* private Font font;
|
||||||
|
*
|
||||||
|
* public FontBuilder(String fontName) {
|
||||||
|
* this.font = new Font(fontName, Font.PLAIN, 12);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public FontBuilder bold() {
|
||||||
|
* this.font = this.font.deriveFont(Font.BOLD);
|
||||||
|
* return this; // Reference returned so calls can be chained
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public FontBuilder size(float pointSize) {
|
||||||
|
* this.font = this.font.deriveFont(pointSize);
|
||||||
|
* return this; // Reference returned so calls can be chained
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Other Font construction methods
|
||||||
|
*
|
||||||
|
* public Font build() {
|
||||||
|
* return this.font;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
* Example Builder Usage:
|
||||||
|
* <pre><code>
|
||||||
|
* Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
|
||||||
|
* .size(14.0f)
|
||||||
|
* .build();
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param <T> the type of object that the builder will construct or compute.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Builder<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a reference to the object being constructed or result being
|
||||||
|
* calculated by the builder.
|
||||||
|
*
|
||||||
|
* @return the object constructed or result calculated by the builder.
|
||||||
|
*/
|
||||||
|
T build();
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
117
after/src/main/java/org/apache/commons/lang3/builder/Diff.java
Normal file
117
after/src/main/java/org/apache/commons/lang3/builder/Diff.java
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A {@code Diff} contains the differences between two {@link Diffable} class
|
||||||
|
* fields.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Typically, {@code Diff}s are retrieved by using a {@link DiffBuilder} to
|
||||||
|
* produce a {@link DiffResult}, containing the differences between two objects.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* The type of object contained within this {@code Diff}. Differences
|
||||||
|
* between primitive objects are stored as their Object wrapper
|
||||||
|
* equivalent.
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public abstract class Diff<T> extends Pair<T, T> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
private final String fieldName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructs a new {@code Diff} for the given field name.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the name of the field
|
||||||
|
*/
|
||||||
|
protected Diff(final String fieldName) {
|
||||||
|
this.type = ObjectUtils.defaultIfNull(
|
||||||
|
TypeUtils.getTypeArguments(getClass(), Diff.class).get(
|
||||||
|
Diff.class.getTypeParameters()[0]), Object.class);
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the type of the field.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the field type
|
||||||
|
*/
|
||||||
|
public final Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the name of the field.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the field name
|
||||||
|
*/
|
||||||
|
public final String getFieldName() {
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns a {@code String} representation of the {@code Diff}, with the
|
||||||
|
* following format:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* [fieldname: left-value, right-value]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return the string representation
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return String.format("[%s: %s, %s]", fieldName, getLeft(), getRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Throws {@code UnsupportedOperationException}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* ignored
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final T setValue(final T value) {
|
||||||
|
throw new UnsupportedOperationException("Cannot alter Diff object.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,983 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Assists in implementing {@link Diffable#diff(Object)} methods.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To use this class, write code as follows:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Person implements Diffable<Person> {
|
||||||
|
* String name;
|
||||||
|
* int age;
|
||||||
|
* boolean smoker;
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public DiffResult diff(Person obj) {
|
||||||
|
* // No need for null check, as NullPointerException correct if obj is null
|
||||||
|
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||||
|
* .append("name", this.name, obj.name)
|
||||||
|
* .append("age", this.age, obj.age)
|
||||||
|
* .append("smoker", this.smoker, obj.smoker)
|
||||||
|
* .build();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@code ToStringStyle} passed to the constructor is embedded in the
|
||||||
|
* returned {@code DiffResult} and influences the style of the
|
||||||
|
* {@code DiffResult.toString()} method. This style choice can be overridden by
|
||||||
|
* calling {@link DiffResult#toString(ToStringStyle)}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T> type of the left and right object.
|
||||||
|
* @see Diffable
|
||||||
|
* @see Diff
|
||||||
|
* @see DiffResult
|
||||||
|
* @see ToStringStyle
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class DiffBuilder<T> implements Builder<DiffResult<T>> {
|
||||||
|
|
||||||
|
private final List<Diff<?>> diffs;
|
||||||
|
private final boolean objectsTriviallyEqual;
|
||||||
|
private final T left;
|
||||||
|
private final T right;
|
||||||
|
private final ToStringStyle style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructs a builder for the specified objects with the specified style.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
|
||||||
|
* not evaluate any calls to {@code append(...)} and will return an empty
|
||||||
|
* {@link DiffResult} when {@link #build()} is executed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs
|
||||||
|
* {@code this} object
|
||||||
|
* @param rhs
|
||||||
|
* the object to diff against
|
||||||
|
* @param style
|
||||||
|
* the style will use when outputting the objects, {@code null}
|
||||||
|
* uses the default
|
||||||
|
* @param testTriviallyEqual
|
||||||
|
* If true, this will test if lhs and rhs are the same or equal.
|
||||||
|
* All of the append(fieldName, lhs, rhs) methods will abort
|
||||||
|
* without creating a field {@link Diff} if the trivially equal
|
||||||
|
* test is enabled and returns true. The result of this test
|
||||||
|
* is never changed throughout the life of this {@link DiffBuilder}.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code lhs} or {@code rhs} is {@code null}
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public DiffBuilder(final T lhs, final T rhs,
|
||||||
|
final ToStringStyle style, final boolean testTriviallyEqual) {
|
||||||
|
|
||||||
|
Validate.notNull(lhs, "lhs");
|
||||||
|
Validate.notNull(rhs, "rhs");
|
||||||
|
|
||||||
|
this.diffs = new ArrayList<>();
|
||||||
|
this.left = lhs;
|
||||||
|
this.right = rhs;
|
||||||
|
this.style = style;
|
||||||
|
|
||||||
|
// Don't compare any fields if objects equal
|
||||||
|
this.objectsTriviallyEqual = testTriviallyEqual && (lhs == rhs || lhs.equals(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructs a builder for the specified objects with the specified style.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
|
||||||
|
* not evaluate any calls to {@code append(...)} and will return an empty
|
||||||
|
* {@link DiffResult} when {@link #build()} is executed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
|
||||||
|
* with the testTriviallyEqual flag enabled.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs
|
||||||
|
* {@code this} object
|
||||||
|
* @param rhs
|
||||||
|
* the object to diff against
|
||||||
|
* @param style
|
||||||
|
* the style will use when outputting the objects, {@code null}
|
||||||
|
* uses the default
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code lhs} or {@code rhs} is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder(final T lhs, final T rhs,
|
||||||
|
final ToStringStyle style) {
|
||||||
|
|
||||||
|
this(lhs, rhs, style, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code boolean}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code boolean}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code boolean}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final boolean lhs,
|
||||||
|
final boolean rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs != rhs) {
|
||||||
|
diffs.add(new Diff<Boolean>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean getLeft() {
|
||||||
|
return Boolean.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean getRight() {
|
||||||
|
return Boolean.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code boolean[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code boolean[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code boolean[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final boolean[] lhs,
|
||||||
|
final boolean[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Boolean[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code byte}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code byte}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code byte}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final byte lhs,
|
||||||
|
final byte rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs != rhs) {
|
||||||
|
diffs.add(new Diff<Byte>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Byte getLeft() {
|
||||||
|
return Byte.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Byte getRight() {
|
||||||
|
return Byte.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code byte[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code byte[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code byte[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final byte[] lhs,
|
||||||
|
final byte[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Byte[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Byte[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Byte[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code char}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code char}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code char}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final char lhs,
|
||||||
|
final char rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs != rhs) {
|
||||||
|
diffs.add(new Diff<Character>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Character getLeft() {
|
||||||
|
return Character.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Character getRight() {
|
||||||
|
return Character.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code char[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code char[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code char[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final char[] lhs,
|
||||||
|
final char[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Character[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Character[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Character[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code double}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code double}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code double}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final double lhs,
|
||||||
|
final double rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) {
|
||||||
|
diffs.add(new Diff<Double>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double getLeft() {
|
||||||
|
return Double.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double getRight() {
|
||||||
|
return Double.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code double[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code double[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code double[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final double[] lhs,
|
||||||
|
final double[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Double[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code float}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code float}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code float}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final float lhs,
|
||||||
|
final float rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) {
|
||||||
|
diffs.add(new Diff<Float>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float getLeft() {
|
||||||
|
return Float.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float getRight() {
|
||||||
|
return Float.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code float[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code float[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code float[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final float[] lhs,
|
||||||
|
final float[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Float[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code int}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code int}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code int}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final int lhs,
|
||||||
|
final int rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs != rhs) {
|
||||||
|
diffs.add(new Diff<Integer>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getLeft() {
|
||||||
|
return Integer.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getRight() {
|
||||||
|
return Integer.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code int[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code int[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code int[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final int[] lhs,
|
||||||
|
final int[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Integer[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code long}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code long}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code long}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final long lhs,
|
||||||
|
final long rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs != rhs) {
|
||||||
|
diffs.add(new Diff<Long>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLeft() {
|
||||||
|
return Long.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getRight() {
|
||||||
|
return Long.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code long[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code long[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code long[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final long[] lhs,
|
||||||
|
final long[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Long[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code short}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code short}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code short}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final short lhs,
|
||||||
|
final short rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs != rhs) {
|
||||||
|
diffs.add(new Diff<Short>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Short getLeft() {
|
||||||
|
return Short.valueOf(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Short getRight() {
|
||||||
|
return Short.valueOf(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code short[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code short[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code short[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final short[] lhs,
|
||||||
|
final short[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Short[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Short[] getLeft() {
|
||||||
|
return ArrayUtils.toObject(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Short[] getRight() {
|
||||||
|
return ArrayUtils.toObject(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code Objects}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code Object}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code Object}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final Object lhs,
|
||||||
|
final Object rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (lhs == rhs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object objectToTest;
|
||||||
|
if (lhs != null) {
|
||||||
|
objectToTest = lhs;
|
||||||
|
} else {
|
||||||
|
// rhs cannot be null, as lhs != rhs
|
||||||
|
objectToTest = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectToTest.getClass().isArray()) {
|
||||||
|
if (objectToTest instanceof boolean[]) {
|
||||||
|
return append(fieldName, (boolean[]) lhs, (boolean[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof byte[]) {
|
||||||
|
return append(fieldName, (byte[]) lhs, (byte[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof char[]) {
|
||||||
|
return append(fieldName, (char[]) lhs, (char[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof double[]) {
|
||||||
|
return append(fieldName, (double[]) lhs, (double[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof float[]) {
|
||||||
|
return append(fieldName, (float[]) lhs, (float[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof int[]) {
|
||||||
|
return append(fieldName, (int[]) lhs, (int[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof long[]) {
|
||||||
|
return append(fieldName, (long[]) lhs, (long[]) rhs);
|
||||||
|
}
|
||||||
|
if (objectToTest instanceof short[]) {
|
||||||
|
return append(fieldName, (short[]) lhs, (short[]) rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(fieldName, (Object[]) lhs, (Object[]) rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not array type
|
||||||
|
if (lhs != null && lhs.equals(rhs)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
diffs.add(new Diff<Object>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLeft() {
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getRight() {
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Test if two {@code Object[]}s are equal.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param lhs
|
||||||
|
* the left hand {@code Object[]}
|
||||||
|
* @param rhs
|
||||||
|
* the right hand {@code Object[]}
|
||||||
|
* @return this
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if field name is {@code null}
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName, final Object[] lhs,
|
||||||
|
final Object[] rhs) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Arrays.equals(lhs, rhs)) {
|
||||||
|
diffs.add(new Diff<Object[]>(fieldName) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getLeft() {
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getRight() {
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append diffs from another {@code DiffResult}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is useful if you want to compare properties which are
|
||||||
|
* themselves Diffable and would like to know which specific part of
|
||||||
|
* it is different.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Person implements Diffable<Person> {
|
||||||
|
* String name;
|
||||||
|
* Address address; // implements Diffable<Address>
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public DiffResult diff(Person obj) {
|
||||||
|
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||||
|
* .append("name", this.name, obj.name)
|
||||||
|
* .append("address", this.address.diff(obj.address))
|
||||||
|
* .build();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* the field name
|
||||||
|
* @param diffResult
|
||||||
|
* the {@code DiffResult} to append
|
||||||
|
* @return this
|
||||||
|
* @throws NullPointerException if field name is {@code null}
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public DiffBuilder<T> append(final String fieldName,
|
||||||
|
final DiffResult<T> diffResult) {
|
||||||
|
validateFieldNameNotNull(fieldName);
|
||||||
|
Validate.notNull(diffResult, "diffResult");
|
||||||
|
if (objectsTriviallyEqual) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Diff<?> diff : diffResult.getDiffs()) {
|
||||||
|
append(fieldName + "." + diff.getFieldName(),
|
||||||
|
diff.getLeft(), diff.getRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@link DiffResult} based on the differences appended to this
|
||||||
|
* builder.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return a {@code DiffResult} containing the differences between the two
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DiffResult<T> build() {
|
||||||
|
return new DiffResult<>(left, right, diffs, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateFieldNameNotNull(final String fieldName) {
|
||||||
|
Validate.notNull(fieldName, "fieldName");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A {@code DiffResult} contains a collection of the differences between two
|
||||||
|
* {@link Diffable} objects. Typically these differences are displayed using
|
||||||
|
* {@link #toString()} method, which returns a string describing the fields that
|
||||||
|
* differ between the objects.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Use a {@link DiffBuilder} to build a {@code DiffResult} comparing two objects.
|
||||||
|
* </p>
|
||||||
|
* @param <T> type of the left and right object.
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class DiffResult<T> implements Iterable<Diff<?>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The {@code String} returned when the objects have no differences:
|
||||||
|
* {@value}
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static final String OBJECTS_SAME_STRING = "";
|
||||||
|
|
||||||
|
private static final String DIFFERS_STRING = "differs from";
|
||||||
|
|
||||||
|
private final List<Diff<?>> diffList;
|
||||||
|
private final T lhs;
|
||||||
|
private final T rhs;
|
||||||
|
private final ToStringStyle style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Creates a {@link DiffResult} containing the differences between two
|
||||||
|
* objects.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lhs
|
||||||
|
* the left hand object
|
||||||
|
* @param rhs
|
||||||
|
* the right hand object
|
||||||
|
* @param diffList
|
||||||
|
* the list of differences, may be empty
|
||||||
|
* @param style
|
||||||
|
* the style to use for the {@link #toString()} method. May be
|
||||||
|
* {@code null}, in which case
|
||||||
|
* {@link ToStringStyle#DEFAULT_STYLE} is used
|
||||||
|
* @throws NullPointerException if {@code lhs}, {@code rhs} or {@code diffs} is {@code null}
|
||||||
|
*/
|
||||||
|
DiffResult(final T lhs, final T rhs, final List<Diff<?>> diffList,
|
||||||
|
final ToStringStyle style) {
|
||||||
|
Validate.notNull(lhs, "lhs");
|
||||||
|
Validate.notNull(rhs, "rhs");
|
||||||
|
Validate.notNull(diffList, "diffList");
|
||||||
|
|
||||||
|
this.diffList = diffList;
|
||||||
|
this.lhs = lhs;
|
||||||
|
this.rhs = rhs;
|
||||||
|
|
||||||
|
if (style == null) {
|
||||||
|
this.style = ToStringStyle.DEFAULT_STYLE;
|
||||||
|
} else {
|
||||||
|
this.style = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the object the right object has been compared to.</p>
|
||||||
|
*
|
||||||
|
* @return the left object of the diff
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public T getLeft() {
|
||||||
|
return this.lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the object the left object has been compared to.</p>
|
||||||
|
*
|
||||||
|
* @return the right object of the diff
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public T getRight() {
|
||||||
|
return this.rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns an unmodifiable list of {@code Diff}s. The list may be empty if
|
||||||
|
* there were no differences between the objects.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return an unmodifiable list of {@code Diff}s
|
||||||
|
*/
|
||||||
|
public List<Diff<?>> getDiffs() {
|
||||||
|
return Collections.unmodifiableList(diffList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the number of differences between the two objects.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the number of differences
|
||||||
|
*/
|
||||||
|
public int getNumberOfDiffs() {
|
||||||
|
return diffList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the style used by the {@link #toString()} method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the style
|
||||||
|
*/
|
||||||
|
public ToStringStyle getToStringStyle() {
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code String} description of the differences contained within
|
||||||
|
* this {@code DiffResult}. A {@link ToStringBuilder} is used for each object
|
||||||
|
* and the style of the output is governed by the {@code ToStringStyle}
|
||||||
|
* passed to the constructor.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If there are no differences stored in this list, the method will return
|
||||||
|
* {@link #OBJECTS_SAME_STRING}. Otherwise, using the example given in
|
||||||
|
* {@link Diffable} and {@link ToStringStyle#SHORT_PREFIX_STYLE}, an output
|
||||||
|
* might be:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Person[name=John Doe,age=32] differs from Person[name=Joe Bloggs,age=26]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This indicates that the objects differ in name and age, but not in
|
||||||
|
* smoking status.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To use a different {@code ToStringStyle} for an instance of this class,
|
||||||
|
* use {@link #toString(ToStringStyle)}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return a {@code String} description of the differences.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code String} description of the differences contained within
|
||||||
|
* this {@code DiffResult}, using the supplied {@code ToStringStyle}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param style
|
||||||
|
* the {@code ToStringStyle} to use when outputting the objects
|
||||||
|
*
|
||||||
|
* @return a {@code String} description of the differences.
|
||||||
|
*/
|
||||||
|
public String toString(final ToStringStyle style) {
|
||||||
|
if (diffList.isEmpty()) {
|
||||||
|
return OBJECTS_SAME_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ToStringBuilder lhsBuilder = new ToStringBuilder(lhs, style);
|
||||||
|
final ToStringBuilder rhsBuilder = new ToStringBuilder(rhs, style);
|
||||||
|
|
||||||
|
for (final Diff<?> diff : diffList) {
|
||||||
|
lhsBuilder.append(diff.getFieldName(), diff.getLeft());
|
||||||
|
rhsBuilder.append(diff.getFieldName(), diff.getRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("%s %s %s", lhsBuilder.build(), DIFFERS_STRING,
|
||||||
|
rhsBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns an iterator over the {@code Diff} objects contained in this list.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the iterator
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Diff<?>> iterator() {
|
||||||
|
return diffList.iterator();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code Diffable} classes can be compared with other objects
|
||||||
|
* for differences. The {@link DiffResult} object retrieved can be queried
|
||||||
|
* for a list of differences or printed using the {@link DiffResult#toString()}.</p>
|
||||||
|
*
|
||||||
|
* <p>The calculation of the differences is <i>consistent with equals</i> if
|
||||||
|
* and only if {@code d1.equals(d2)} implies {@code d1.diff(d2) == ""}.
|
||||||
|
* It is strongly recommended that implementations are consistent with equals
|
||||||
|
* to avoid confusion. Note that {@code null} is not an instance of any class
|
||||||
|
* and {@code d1.diff(null)} should throw a {@code NullPointerException}.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code Diffable} classes lend themselves well to unit testing, in which a
|
||||||
|
* easily readable description of the differences between an anticipated result and
|
||||||
|
* an actual result can be retrieved. For example:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* Assert.assertEquals(expected.diff(result), expected, result);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param <T> the type of objects that this object may be differentiated against
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Diffable<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Retrieves a list of the differences between
|
||||||
|
* this object and the supplied object.</p>
|
||||||
|
*
|
||||||
|
* @param obj the object to diff against, can be {@code null}
|
||||||
|
* @return a list of differences
|
||||||
|
* @throws NullPointerException if the specified object is {@code null}
|
||||||
|
*/
|
||||||
|
DiffResult<T> diff(T obj);
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
36
after/src/main/java/org/apache/commons/lang3/builder/EqualsExclude.java
Executable file
36
after/src/main/java/org/apache/commons/lang3/builder/EqualsExclude.java
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this annotation to exclude a field from being used by
|
||||||
|
* the various {@code reflectionEquals} methods defined on
|
||||||
|
* {@link EqualsBuilder}.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface EqualsExclude {
|
||||||
|
// empty
|
||||||
|
}
|
|
@ -0,0 +1,994 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArraySorter;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Assists in implementing {@link Object#hashCode()} methods.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class enables a good {@code hashCode} method to be built for any class. It follows the rules laid out in
|
||||||
|
* the book <a href="http://www.oracle.com/technetwork/java/effectivejava-136174.html">Effective Java</a> by Joshua Bloch. Writing a
|
||||||
|
* good {@code hashCode} method is actually quite difficult. This class aims to simplify the process.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The following is the approach taken. When appending a data field, the current total is multiplied by the
|
||||||
|
* multiplier then a relevant value
|
||||||
|
* for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then
|
||||||
|
* appending the integer 45 will create a hash code of 674, namely 17 * 37 + 45.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* All relevant fields from the object should be included in the {@code hashCode} method. Derived fields may be
|
||||||
|
* excluded. In general, any field used in the {@code equals} method must be used in the {@code hashCode}
|
||||||
|
* method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To use this class write code as follows:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Person {
|
||||||
|
* String name;
|
||||||
|
* int age;
|
||||||
|
* boolean smoker;
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public int hashCode() {
|
||||||
|
* // you pick a hard-coded, randomly chosen, non-zero, odd number
|
||||||
|
* // ideally different for each class
|
||||||
|
* return new HashCodeBuilder(17, 37).
|
||||||
|
* append(name).
|
||||||
|
* append(age).
|
||||||
|
* append(smoker).
|
||||||
|
* toHashCode();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If required, the superclass {@code hashCode()} can be added using {@link #appendSuper}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
|
||||||
|
* usually private, the method, {@code reflectionHashCode}, uses {@code AccessibleObject.setAccessible}
|
||||||
|
* to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
|
||||||
|
* are set up correctly. It is also slower than testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A typical invocation for this method would look like:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public int hashCode() {
|
||||||
|
* return HashCodeBuilder.reflectionHashCode(this);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>The {@link HashCodeExclude} annotation can be used to exclude fields from being
|
||||||
|
* used by the {@code reflectionHashCode} methods.</p>
|
||||||
|
*
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class HashCodeBuilder implements Builder<Integer> {
|
||||||
|
/**
|
||||||
|
* The default initial value to use in reflection hash code building.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_INITIAL_VALUE = 17;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default multiplier value to use in reflection hash code building.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_MULTIPLIER_VALUE = 37;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
||||||
|
* we are in the process of calculating.
|
||||||
|
*
|
||||||
|
* So we generate a one-to-one mapping from the original object to a new object.
|
||||||
|
*
|
||||||
|
* Now HashSet uses equals() to determine if two elements with the same hash code really
|
||||||
|
* are equal, so we also need to ensure that the replacement objects are only equal
|
||||||
|
* if the original objects are identical.
|
||||||
|
*
|
||||||
|
* The original implementation (2.4 and before) used the System.identityHashCode()
|
||||||
|
* method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
|
||||||
|
*
|
||||||
|
* We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
|
||||||
|
* to disambiguate the duplicate ids.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the registry of objects being traversed by the reflection methods in the current thread.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Set the registry of objects being traversed
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
static Set<IDKey> getRegistry() {
|
||||||
|
return REGISTRY.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns {@code true} if the registry contains the given object. Used by the reflection methods to avoid
|
||||||
|
* infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The object to lookup in the registry.
|
||||||
|
* @return boolean {@code true} if the registry contains the given object.
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
static boolean isRegistered(final Object value) {
|
||||||
|
final Set<IDKey> registry = getRegistry();
|
||||||
|
return registry != null && registry.contains(new IDKey(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Appends the fields and values defined by the given object of the given {@code Class}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the object to append details of
|
||||||
|
* @param clazz
|
||||||
|
* the class to append details of
|
||||||
|
* @param builder
|
||||||
|
* the builder to append to
|
||||||
|
* @param useTransients
|
||||||
|
* whether to use transient fields
|
||||||
|
* @param excludeFields
|
||||||
|
* Collection of String field names to exclude from use in calculation of hash code
|
||||||
|
*/
|
||||||
|
private static void reflectionAppend(final Object object, final Class<?> clazz, final HashCodeBuilder builder, final boolean useTransients,
|
||||||
|
final String[] excludeFields) {
|
||||||
|
if (isRegistered(object)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
register(object);
|
||||||
|
// The elements in the returned array are not sorted and are not in any particular order.
|
||||||
|
final Field[] fields = ArraySorter.sort(clazz.getDeclaredFields(), Comparator.comparing(Field::getName));
|
||||||
|
AccessibleObject.setAccessible(fields, true);
|
||||||
|
for (final Field field : fields) {
|
||||||
|
if (!ArrayUtils.contains(excludeFields, field.getName())
|
||||||
|
&& !field.getName().contains("$")
|
||||||
|
&& (useTransients || !Modifier.isTransient(field.getModifiers()))
|
||||||
|
&& !Modifier.isStatic(field.getModifiers())
|
||||||
|
&& !field.isAnnotationPresent(HashCodeExclude.class)) {
|
||||||
|
try {
|
||||||
|
final Object fieldValue = field.get(object);
|
||||||
|
builder.append(fieldValue);
|
||||||
|
} catch (final IllegalAccessException e) {
|
||||||
|
// this can't happen. Would get a Security exception instead
|
||||||
|
// throw a runtime exception in case the impossible happens.
|
||||||
|
throw new InternalError("Unexpected IllegalAccessException");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
unregister(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||||
|
* {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initialNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the initial value. This will be the returned
|
||||||
|
* value if no fields are found to include in the hash code
|
||||||
|
* @param multiplierNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the multiplier
|
||||||
|
* @param object
|
||||||
|
* the Object to create a {@code hashCode} for
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is zero or even
|
||||||
|
*
|
||||||
|
* @see HashCodeExclude
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object) {
|
||||||
|
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the TestTransients parameter is set to {@code true}, transient members will be tested, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initialNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the initial value. This will be the returned
|
||||||
|
* value if no fields are found to include in the hash code
|
||||||
|
* @param multiplierNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the multiplier
|
||||||
|
* @param object
|
||||||
|
* the Object to create a {@code hashCode} for
|
||||||
|
* @param testTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is zero or even
|
||||||
|
*
|
||||||
|
* @see HashCodeExclude
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final Object object,
|
||||||
|
final boolean testTransients) {
|
||||||
|
return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the TestTransients parameter is set to {@code true}, transient members will be tested, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be included. Superclass fields will be included up to and including the specified
|
||||||
|
* superclass. A null superclass is treated as java.lang.Object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital. Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object involved
|
||||||
|
* @param initialNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the initial value. This will be the returned
|
||||||
|
* value if no fields are found to include in the hash code
|
||||||
|
* @param multiplierNonZeroOddNumber
|
||||||
|
* a non-zero, odd number used as the multiplier
|
||||||
|
* @param object
|
||||||
|
* the Object to create a {@code hashCode} for
|
||||||
|
* @param testTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param reflectUpToClass
|
||||||
|
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||||
|
* @param excludeFields
|
||||||
|
* array of field names to exclude from use in calculation of hash code
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is zero or even
|
||||||
|
*
|
||||||
|
* @see HashCodeExclude
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static <T> int reflectionHashCode(final int initialNonZeroOddNumber, final int multiplierNonZeroOddNumber, final T object,
|
||||||
|
final boolean testTransients, final Class<? super T> reflectUpToClass, final String... excludeFields) {
|
||||||
|
Validate.notNull(object, "object");
|
||||||
|
final HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
|
||||||
|
Class<?> clazz = object.getClass();
|
||||||
|
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||||
|
while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
reflectionAppend(object, clazz, builder, testTransients, excludeFields);
|
||||||
|
}
|
||||||
|
return builder.toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* If the TestTransients parameter is set to {@code true}, transient members will be tested, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||||
|
* in the hash code, the result of this method will be constant.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to create a {@code hashCode} for
|
||||||
|
* @param testTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the object is {@code null}
|
||||||
|
*
|
||||||
|
* @see HashCodeExclude
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final Object object, final boolean testTransients) {
|
||||||
|
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object,
|
||||||
|
testTransients, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||||
|
* {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||||
|
* in the hash code, the result of this method will be constant.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to create a {@code hashCode} for
|
||||||
|
* @param excludeFields
|
||||||
|
* Collection of String field names to exclude from use in calculation of hash code
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the object is {@code null}
|
||||||
|
*
|
||||||
|
* @see HashCodeExclude
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final Object object, final Collection<String> excludeFields) {
|
||||||
|
return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses reflection to build a valid hash code from the fields of {@code object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor uses two hard coded choices for the constants needed to build a hash code.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be used, as they are likely derived fields, and not part of the value of the
|
||||||
|
* {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be tested. Superclass fields will be included. If no fields are found to include
|
||||||
|
* in the hash code, the result of this method will be constant.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to create a {@code hashCode} for
|
||||||
|
* @param excludeFields
|
||||||
|
* array of field names to exclude from use in calculation of hash code
|
||||||
|
* @return int hash code
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the object is {@code null}
|
||||||
|
*
|
||||||
|
* @see HashCodeExclude
|
||||||
|
*/
|
||||||
|
public static int reflectionHashCode(final Object object, final String... excludeFields) {
|
||||||
|
return reflectionHashCode(DEFAULT_INITIAL_VALUE, DEFAULT_MULTIPLIER_VALUE, object, false,
|
||||||
|
null, excludeFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Registers the given object. Used by the reflection methods to avoid infinite loops.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The object to register.
|
||||||
|
*/
|
||||||
|
private static void register(final Object value) {
|
||||||
|
Set<IDKey> registry = getRegistry();
|
||||||
|
if (registry == null) {
|
||||||
|
registry = new HashSet<>();
|
||||||
|
REGISTRY.set(registry);
|
||||||
|
}
|
||||||
|
registry.add(new IDKey(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Unregisters the given object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The object to unregister.
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
private static void unregister(final Object value) {
|
||||||
|
final Set<IDKey> registry = getRegistry();
|
||||||
|
if (registry != null) {
|
||||||
|
registry.remove(new IDKey(value));
|
||||||
|
if (registry.isEmpty()) {
|
||||||
|
REGISTRY.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant to use in building the hashCode.
|
||||||
|
*/
|
||||||
|
private final int iConstant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Running total of the hashCode.
|
||||||
|
*/
|
||||||
|
private int iTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Uses two hard coded choices for the constants needed to build a {@code hashCode}.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder() {
|
||||||
|
iConstant = 37;
|
||||||
|
iTotal = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Two randomly chosen, odd numbers must be passed in. Ideally these should be different for each class,
|
||||||
|
* however this is not vital.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Prime numbers are preferred, especially for the multiplier.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initialOddNumber
|
||||||
|
* an odd number used as the initial value
|
||||||
|
* @param multiplierOddNumber
|
||||||
|
* an odd number used as the multiplier
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the number is even
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder(final int initialOddNumber, final int multiplierOddNumber) {
|
||||||
|
Validate.isTrue(initialOddNumber % 2 != 0, "HashCodeBuilder requires an odd initial value");
|
||||||
|
Validate.isTrue(multiplierOddNumber % 2 != 0, "HashCodeBuilder requires an odd multiplier");
|
||||||
|
iConstant = multiplierOddNumber;
|
||||||
|
iTotal = initialOddNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code boolean}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This adds {@code 1} when true, and {@code 0} when false to the {@code hashCode}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This is in contrast to the standard {@code java.lang.Boolean.hashCode} handling, which computes
|
||||||
|
* a {@code hashCode} value of {@code 1231} for {@code java.lang.Boolean} instances
|
||||||
|
* that represent {@code true} or {@code 1237} for {@code java.lang.Boolean} instances
|
||||||
|
* that represent {@code false}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This is in accordance with the <i>Effective Java</i> design.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the boolean to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final boolean value) {
|
||||||
|
iTotal = iTotal * iConstant + (value ? 0 : 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code boolean} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final boolean[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final boolean element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code byte}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the byte to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final byte value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code byte} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final byte[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final byte element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code char}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the char to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final char value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code char} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final char[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final char element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code double}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the double to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final double value) {
|
||||||
|
return append(Double.doubleToLongBits(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code double} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final double[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final double element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code float}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the float to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final float value) {
|
||||||
|
iTotal = iTotal * iConstant + Float.floatToIntBits(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code float} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final float[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final float element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for an {@code int}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the int to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final int value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for an {@code int} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final int[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final int element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code long}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the long to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
// NOTE: This method uses >> and not >>> as Effective Java and
|
||||||
|
// Long.hashCode do. Ideally we should switch to >>> at
|
||||||
|
// some stage. There are backwards compat issues, so
|
||||||
|
// that will have to wait for the time being. cf LANG-342.
|
||||||
|
public HashCodeBuilder append(final long value) {
|
||||||
|
iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code long} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final long[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final long element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for an {@code Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final Object object) {
|
||||||
|
if (object == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
|
||||||
|
} else if (object.getClass().isArray()) {
|
||||||
|
// factor out array case in order to keep method small enough
|
||||||
|
// to be inlined
|
||||||
|
appendArray(object);
|
||||||
|
} else {
|
||||||
|
iTotal = iTotal * iConstant + object.hashCode();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for an array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
*/
|
||||||
|
private void appendArray(final Object object) {
|
||||||
|
// 'Switch' on type of array, to dispatch to the correct handler
|
||||||
|
// This handles multi dimensional arrays
|
||||||
|
if (object instanceof long[]) {
|
||||||
|
append((long[]) object);
|
||||||
|
} else if (object instanceof int[]) {
|
||||||
|
append((int[]) object);
|
||||||
|
} else if (object instanceof short[]) {
|
||||||
|
append((short[]) object);
|
||||||
|
} else if (object instanceof char[]) {
|
||||||
|
append((char[]) object);
|
||||||
|
} else if (object instanceof byte[]) {
|
||||||
|
append((byte[]) object);
|
||||||
|
} else if (object instanceof double[]) {
|
||||||
|
append((double[]) object);
|
||||||
|
} else if (object instanceof float[]) {
|
||||||
|
append((float[]) object);
|
||||||
|
} else if (object instanceof boolean[]) {
|
||||||
|
append((boolean[]) object);
|
||||||
|
} else {
|
||||||
|
// Not an array of primitives
|
||||||
|
append((Object[]) object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for an {@code Object} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final Object[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final Object element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code short}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the short to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final short value) {
|
||||||
|
iTotal = iTotal * iConstant + value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append a {@code hashCode} for a {@code short} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code hashCode}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder append(final short[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
iTotal = iTotal * iConstant;
|
||||||
|
} else {
|
||||||
|
for (final short element : array) {
|
||||||
|
append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Adds the result of super.hashCode() to this builder.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param superHashCode
|
||||||
|
* the result of calling {@code super.hashCode()}
|
||||||
|
* @return this HashCodeBuilder, used to chain calls.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public HashCodeBuilder appendSuper(final int superHashCode) {
|
||||||
|
iTotal = iTotal * iConstant + superHashCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Returns the computed {@code hashCode}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return {@code hashCode} based on the fields appended
|
||||||
|
*/
|
||||||
|
public int toHashCode() {
|
||||||
|
return iTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the computed {@code hashCode}.
|
||||||
|
*
|
||||||
|
* @return {@code hashCode} based on the fields appended
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer build() {
|
||||||
|
return Integer.valueOf(toHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The computed {@code hashCode} from toHashCode() is returned due to the likelihood
|
||||||
|
* of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for
|
||||||
|
* HashCodeBuilder itself is.</p>
|
||||||
|
*
|
||||||
|
* @return {@code hashCode} based on the fields appended
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
after/src/main/java/org/apache/commons/lang3/builder/HashCodeExclude.java
Executable file
36
after/src/main/java/org/apache/commons/lang3/builder/HashCodeExclude.java
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this annotation to exclude a field from being used by
|
||||||
|
* the various {@code reflectionHashcode} methods defined on
|
||||||
|
* {@link HashCodeBuilder}.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface HashCodeExclude {
|
||||||
|
// empty
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
// adapted from org.apache.axis.utils.IDKey
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap an identity key (System.identityHashCode())
|
||||||
|
* so that an object can only be equal() to itself.
|
||||||
|
*
|
||||||
|
* This is necessary to disambiguate the occasional duplicate
|
||||||
|
* identityHashCodes that can occur.
|
||||||
|
*/
|
||||||
|
final class IDKey {
|
||||||
|
private final Object value;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for IDKey
|
||||||
|
* @param _value The value
|
||||||
|
*/
|
||||||
|
IDKey(final Object _value) {
|
||||||
|
// This is the Object hash code
|
||||||
|
id = System.identityHashCode(_value);
|
||||||
|
// There have been some cases (LANG-459) that return the
|
||||||
|
// same identity hash code for different objects. So
|
||||||
|
// the value is also added to disambiguate these cases.
|
||||||
|
value = _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns hash code - i.e. the system identity hashcode.
|
||||||
|
* @return the hashcode
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks if instances are equal
|
||||||
|
* @param other The other object to compare to
|
||||||
|
* @return if the instances are for the same object
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object other) {
|
||||||
|
if (!(other instanceof IDKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final IDKey idKey = (IDKey) other;
|
||||||
|
if (id != idKey.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Note that identity equals is used.
|
||||||
|
return value == idKey.value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Works with {@link ToStringBuilder} to create a "deep" {@code toString}.
|
||||||
|
* But instead a single line like the {@link RecursiveToStringStyle} this creates a multiline String
|
||||||
|
* similar to the {@link ToStringStyle#MULTI_LINE_STYLE}.</p>
|
||||||
|
*
|
||||||
|
* <p>To use this class write code as follows:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Job {
|
||||||
|
* String title;
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public class Person {
|
||||||
|
* String name;
|
||||||
|
* int age;
|
||||||
|
* boolean smoker;
|
||||||
|
* Job job;
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public String toString() {
|
||||||
|
* return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This will produce a toString of the format:<br>
|
||||||
|
* <code>Person@7f54[ <br>
|
||||||
|
* name=Stephen, <br>
|
||||||
|
* age=29, <br>
|
||||||
|
* smoker=false, <br>
|
||||||
|
* job=Job@43cd2[ <br>
|
||||||
|
* title=Manager <br>
|
||||||
|
* ] <br>
|
||||||
|
* ]
|
||||||
|
* </code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support.
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Indenting of inner lines. */
|
||||||
|
private static final int INDENT = 2;
|
||||||
|
|
||||||
|
/** Current indenting. */
|
||||||
|
private int spaces = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public MultilineRecursiveToStringStyle() {
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the fields responsible for the line breaks and indenting.
|
||||||
|
* Must be invoked after changing the {@link #spaces} value.
|
||||||
|
*/
|
||||||
|
private void resetIndent() {
|
||||||
|
setArrayStart("{" + System.lineSeparator() + spacer(spaces));
|
||||||
|
setArraySeparator("," + System.lineSeparator() + spacer(spaces));
|
||||||
|
setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
|
||||||
|
|
||||||
|
setContentStart("[" + System.lineSeparator() + spacer(spaces));
|
||||||
|
setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
|
||||||
|
setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a StringBuilder responsible for the indenting.
|
||||||
|
*
|
||||||
|
* @param spaces how far to indent
|
||||||
|
* @return a StringBuilder with {spaces} leading space characters.
|
||||||
|
*/
|
||||||
|
private StringBuilder spacer(final int spaces) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < spaces; i++) {
|
||||||
|
sb.append(" ");
|
||||||
|
}
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
|
||||||
|
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
|
||||||
|
&& accept(value.getClass())) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
buffer.append(ReflectionToStringBuilder.toString(value, this));
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
} else {
|
||||||
|
super.appendDetail(buffer, fieldName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.reflectionAppendArrayDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
|
||||||
|
spaces += INDENT;
|
||||||
|
resetIndent();
|
||||||
|
super.appendDetail(buffer, fieldName, array);
|
||||||
|
spaces -= INDENT;
|
||||||
|
resetIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Works with {@link ToStringBuilder} to create a "deep" {@code toString}.</p>
|
||||||
|
*
|
||||||
|
* <p>To use this class write code as follows:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Job {
|
||||||
|
* String title;
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public class Person {
|
||||||
|
* String name;
|
||||||
|
* int age;
|
||||||
|
* boolean smoker;
|
||||||
|
* Job job;
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public String toString() {
|
||||||
|
* return new ReflectionToStringBuilder(this, new RecursiveToStringStyle()).toString();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>This will produce a toString of the format:
|
||||||
|
* {@code Person@7f54[name=Stephen,age=29,smoker=false,job=Job@43cd2[title=Manager]]}</p>
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
public class RecursiveToStringStyle extends ToStringStyle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support.
|
||||||
|
*
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructor.</p>
|
||||||
|
*/
|
||||||
|
public RecursiveToStringStyle() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
|
||||||
|
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) &&
|
||||||
|
!String.class.equals(value.getClass()) &&
|
||||||
|
accept(value.getClass())) {
|
||||||
|
buffer.append(ReflectionToStringBuilder.toString(value, this));
|
||||||
|
} else {
|
||||||
|
super.appendDetail(buffer, fieldName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
|
||||||
|
appendClassName(buffer, coll);
|
||||||
|
appendIdentityHashCode(buffer, coll);
|
||||||
|
appendDetail(buffer, fieldName, coll.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not to recursively format the given {@code Class}.
|
||||||
|
* By default, this method always returns {@code true}, but may be overwritten by
|
||||||
|
* sub-classes to filter specific classes.
|
||||||
|
*
|
||||||
|
* @param clazz
|
||||||
|
* The class to test.
|
||||||
|
* @return Whether or not to recursively format the given {@code Class}.
|
||||||
|
*/
|
||||||
|
protected boolean accept(final Class<?> clazz) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.reflect.FieldUtils.readField;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ClassUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Assists in implementing {@link Diffable#diff(Object)} methods.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* All non-static, non-transient fields (including inherited fields)
|
||||||
|
* of the objects to diff are discovered using reflection and compared
|
||||||
|
* for differences.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To use this class, write code as follows:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class Person implements Diffable<Person> {
|
||||||
|
* String name;
|
||||||
|
* int age;
|
||||||
|
* boolean smoker;
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* public DiffResult diff(Person obj) {
|
||||||
|
* // No need for null check, as NullPointerException correct if obj is null
|
||||||
|
* return new ReflectionDiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||||
|
* .build();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@code ToStringStyle} passed to the constructor is embedded in the
|
||||||
|
* returned {@code DiffResult} and influences the style of the
|
||||||
|
* {@code DiffResult.toString()} method. This style choice can be overridden by
|
||||||
|
* calling {@link DiffResult#toString(ToStringStyle)}.
|
||||||
|
* </p>
|
||||||
|
* @param <T>
|
||||||
|
* type of the left and right object to diff.
|
||||||
|
* @see Diffable
|
||||||
|
* @see Diff
|
||||||
|
* @see DiffResult
|
||||||
|
* @see ToStringStyle
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
|
||||||
|
|
||||||
|
private final Object left;
|
||||||
|
private final Object right;
|
||||||
|
private final DiffBuilder<T> diffBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructs a builder for the specified objects with the specified style.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
|
||||||
|
* not evaluate any calls to {@code append(...)} and will return an empty
|
||||||
|
* {@link DiffResult} when {@link #build()} is executed.
|
||||||
|
* </p>
|
||||||
|
* @param lhs
|
||||||
|
* {@code this} object
|
||||||
|
* @param rhs
|
||||||
|
* the object to diff against
|
||||||
|
* @param style
|
||||||
|
* the style will use when outputting the objects, {@code null}
|
||||||
|
* uses the default
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code lhs} or {@code rhs} is {@code null}
|
||||||
|
*/
|
||||||
|
public ReflectionDiffBuilder(final T lhs, final T rhs, final ToStringStyle style) {
|
||||||
|
this.left = lhs;
|
||||||
|
this.right = rhs;
|
||||||
|
diffBuilder = new DiffBuilder<>(lhs, rhs, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DiffResult<T> build() {
|
||||||
|
if (left.equals(right)) {
|
||||||
|
return diffBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
appendFields(left.getClass());
|
||||||
|
return diffBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendFields(final Class<?> clazz) {
|
||||||
|
for (final Field field : FieldUtils.getAllFields(clazz)) {
|
||||||
|
if (accept(field)) {
|
||||||
|
try {
|
||||||
|
diffBuilder.append(field.getName(), readField(field, left, true),
|
||||||
|
readField(field, right, true));
|
||||||
|
} catch (final IllegalAccessException ex) {
|
||||||
|
//this can't happen. Would get a Security exception instead
|
||||||
|
//throw a runtime exception in case the impossible happens.
|
||||||
|
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean accept(final Field field) {
|
||||||
|
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Modifier.isTransient(field.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !Modifier.isStatic(field.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,848 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArraySorter;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.ClassUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Assists in implementing {@link Object#toString()} methods using reflection.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This class uses reflection to determine the fields to append. Because these fields are usually private, the class
|
||||||
|
* uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to
|
||||||
|
* change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are
|
||||||
|
* set up correctly.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Using reflection to access (private) fields circumvents any synchronization protection guarding access to these
|
||||||
|
* fields. If a toString method cannot safely read a field, you should exclude it from the toString method, or use
|
||||||
|
* synchronization consistent with the class' lock management around the invocation of the method. Take special care to
|
||||||
|
* exclude non-thread-safe collection classes, because these classes may throw ConcurrentModificationException if
|
||||||
|
* modified while the toString method is executing.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* A typical invocation for this method would look like:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* public String toString() {
|
||||||
|
* return ReflectionToStringBuilder.toString(this);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* You can also use the builder to debug 3rd party objects:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* A subclass can control field output by overriding the methods:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #accept(java.lang.reflect.Field)}</li>
|
||||||
|
* <li>{@link #getValue(java.lang.reflect.Field)}</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* For example, this method does <i>not</i> include the {@code password} field in the returned {@code String}:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* public String toString() {
|
||||||
|
* return (new ReflectionToStringBuilder(this) {
|
||||||
|
* protected boolean accept(Field f) {
|
||||||
|
* return super.accept(f) && !f.getName().equals("password");
|
||||||
|
* }
|
||||||
|
* }).toString();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Alternatively the {@link ToStringExclude} annotation can be used to exclude fields from being incorporated in the
|
||||||
|
* result.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* It is also possible to use the {@link ToStringSummary} annotation to output the summary information instead of the
|
||||||
|
* detailed information of a field.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The exact format of the {@code toString} is determined by the {@link ToStringStyle} passed into the constructor.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> the default {@link ToStringStyle} will only do a "shallow" formatting, i.e. composed objects are not
|
||||||
|
* further traversed. To get "deep" formatting, use an instance of {@link RecursiveToStringStyle}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class ReflectionToStringBuilder extends ToStringBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code toString} value using the default {@code ToStringStyle} through reflection.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be included, as they are likely derived. Static fields will not be included.
|
||||||
|
* Superclass fields will be appended.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to be output
|
||||||
|
* @return the String result
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
*
|
||||||
|
* @see ToStringExclude
|
||||||
|
* @see ToStringSummary
|
||||||
|
*/
|
||||||
|
public static String toString(final Object object) {
|
||||||
|
return toString(object, null, false, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code toString} value through reflection.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Transient members will be not be included, as they are likely derived. Static fields will not be included.
|
||||||
|
* Superclass fields will be appended.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to be output
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @return the String result
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object or {@code ToStringStyle} is {@code null}
|
||||||
|
*
|
||||||
|
* @see ToStringExclude
|
||||||
|
* @see ToStringSummary
|
||||||
|
*/
|
||||||
|
public static String toString(final Object object, final ToStringStyle style) {
|
||||||
|
return toString(object, style, false, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code toString} value through reflection.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputTransients} is {@code true}, transient members will be output, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be included. Superclass fields will be appended.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to be output
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param outputTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @return the String result
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
*
|
||||||
|
* @see ToStringExclude
|
||||||
|
* @see ToStringSummary
|
||||||
|
*/
|
||||||
|
public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients) {
|
||||||
|
return toString(object, style, outputTransients, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code toString} value through reflection.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputTransients} is {@code true}, transient fields will be output, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are
|
||||||
|
* ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Static fields will not be included. Superclass fields will be appended.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to be output
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param outputTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param outputStatics
|
||||||
|
* whether to include static fields
|
||||||
|
* @return the String result
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
*
|
||||||
|
* @see ToStringExclude
|
||||||
|
* @see ToStringSummary
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public static String toString(final Object object, final ToStringStyle style, final boolean outputTransients, final boolean outputStatics) {
|
||||||
|
return toString(object, style, outputTransients, outputStatics, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code toString} value through reflection.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputTransients} is {@code true}, transient fields will be output, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are
|
||||||
|
* ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
|
||||||
|
* {@code java.lang.Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object
|
||||||
|
* @param object
|
||||||
|
* the Object to be output
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param outputTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param outputStatics
|
||||||
|
* whether to include static fields
|
||||||
|
* @param reflectUpToClass
|
||||||
|
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||||
|
* @return the String result
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
*
|
||||||
|
* @see ToStringExclude
|
||||||
|
* @see ToStringSummary
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public static <T> String toString(
|
||||||
|
final T object, final ToStringStyle style, final boolean outputTransients,
|
||||||
|
final boolean outputStatics, final Class<? super T> reflectUpToClass) {
|
||||||
|
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Builds a {@code toString} value through reflection.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It uses {@code AccessibleObject.setAccessible} to gain access to private fields. This means that it will
|
||||||
|
* throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
|
||||||
|
* also not as efficient as testing explicitly.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputTransients} is {@code true}, transient fields will be output, otherwise they
|
||||||
|
* are ignored, as they are likely derived fields, and not part of the value of the Object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@code outputStatics} is {@code true}, static fields will be output, otherwise they are
|
||||||
|
* ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
|
||||||
|
* {@code java.lang.Object}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default {@code ToStringStyle} is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object
|
||||||
|
* @param object
|
||||||
|
* the Object to be output
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param outputTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param outputStatics
|
||||||
|
* whether to include static fields
|
||||||
|
* @param excludeNullValues
|
||||||
|
* whether to exclude fields whose values are null
|
||||||
|
* @param reflectUpToClass
|
||||||
|
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||||
|
* @return the String result
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object is {@code null}
|
||||||
|
*
|
||||||
|
* @see ToStringExclude
|
||||||
|
* @see ToStringSummary
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public static <T> String toString(
|
||||||
|
final T object, final ToStringStyle style, final boolean outputTransients,
|
||||||
|
final boolean outputStatics, final boolean excludeNullValues, final Class<? super T> reflectUpToClass) {
|
||||||
|
return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics, excludeNullValues)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a String for a toString method excluding the given field names.
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* The object to "toString".
|
||||||
|
* @param excludeFieldNames
|
||||||
|
* The field names to exclude. Null excludes nothing.
|
||||||
|
* @return The toString value.
|
||||||
|
*/
|
||||||
|
public static String toStringExclude(final Object object, final Collection<String> excludeFieldNames) {
|
||||||
|
return toStringExclude(object, toNoNullStringArray(excludeFieldNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given Collection into an array of Strings. The returned array does not contain {@code null}
|
||||||
|
* entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element
|
||||||
|
* is {@code null}.
|
||||||
|
*
|
||||||
|
* @param collection
|
||||||
|
* The collection to convert
|
||||||
|
* @return A new array of Strings.
|
||||||
|
*/
|
||||||
|
static String[] toNoNullStringArray(final Collection<String> collection) {
|
||||||
|
if (collection == null) {
|
||||||
|
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||||
|
}
|
||||||
|
return toNoNullStringArray(collection.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new array of Strings without null elements. Internal method used to normalize exclude lists
|
||||||
|
* (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException}
|
||||||
|
* if an array element is {@code null}.
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* The array to check
|
||||||
|
* @return The given array or a new array without null.
|
||||||
|
*/
|
||||||
|
static String[] toNoNullStringArray(final Object[] array) {
|
||||||
|
final List<String> list = new ArrayList<>(array.length);
|
||||||
|
for (final Object e : array) {
|
||||||
|
if (e != null) {
|
||||||
|
list.add(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a String for a toString method excluding the given field names.
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* The object to "toString".
|
||||||
|
* @param excludeFieldNames
|
||||||
|
* The field names to exclude
|
||||||
|
* @return The toString value.
|
||||||
|
*/
|
||||||
|
public static String toStringExclude(final Object object, final String... excludeFieldNames) {
|
||||||
|
return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object checkNotNull(final Object obj) {
|
||||||
|
return Validate.notNull(obj, "obj");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to append static fields.
|
||||||
|
*/
|
||||||
|
private boolean appendStatics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to append transient fields.
|
||||||
|
*/
|
||||||
|
private boolean appendTransients;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to append fields that are null.
|
||||||
|
*/
|
||||||
|
private boolean excludeNullValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which field names to exclude from output. Intended for fields like {@code "password"}.
|
||||||
|
*
|
||||||
|
* @since 3.0 this is protected instead of private
|
||||||
|
*/
|
||||||
|
protected String[] excludeFieldNames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last super class to stop appending fields for.
|
||||||
|
*/
|
||||||
|
private Class<?> upToClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor outputs using the default style set with {@code setDefaultStyle}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to build a {@code toString} for, must not be {@code null}
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object passed in is {@code null}
|
||||||
|
*/
|
||||||
|
public ReflectionToStringBuilder(final Object object) {
|
||||||
|
super(checkNotNull(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default style is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to build a {@code toString} for, must not be {@code null}
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object passed in is {@code null}
|
||||||
|
*/
|
||||||
|
public ReflectionToStringBuilder(final Object object, final ToStringStyle style) {
|
||||||
|
super(checkNotNull(object), style);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the style is {@code null}, the default style is used.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the buffer is {@code null}, a new one is created.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the Object to build a {@code toString} for
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param buffer
|
||||||
|
* the {@code StringBuffer} to populate, may be {@code null}
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the Object passed in is {@code null}
|
||||||
|
*/
|
||||||
|
public ReflectionToStringBuilder(final Object object, final ToStringStyle style, final StringBuffer buffer) {
|
||||||
|
super(checkNotNull(object), style, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object
|
||||||
|
* @param object
|
||||||
|
* the Object to build a {@code toString} for
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param buffer
|
||||||
|
* the {@code StringBuffer} to populate, may be {@code null}
|
||||||
|
* @param reflectUpToClass
|
||||||
|
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||||
|
* @param outputTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param outputStatics
|
||||||
|
* whether to include static fields
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public <T> ReflectionToStringBuilder(
|
||||||
|
final T object, final ToStringStyle style, final StringBuffer buffer,
|
||||||
|
final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics) {
|
||||||
|
super(checkNotNull(object), style, buffer);
|
||||||
|
this.setUpToClass(reflectUpToClass);
|
||||||
|
this.setAppendTransients(outputTransients);
|
||||||
|
this.setAppendStatics(outputStatics);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of the object
|
||||||
|
* @param object
|
||||||
|
* the Object to build a {@code toString} for
|
||||||
|
* @param style
|
||||||
|
* the style of the {@code toString} to create, may be {@code null}
|
||||||
|
* @param buffer
|
||||||
|
* the {@code StringBuffer} to populate, may be {@code null}
|
||||||
|
* @param reflectUpToClass
|
||||||
|
* the superclass to reflect up to (inclusive), may be {@code null}
|
||||||
|
* @param outputTransients
|
||||||
|
* whether to include transient fields
|
||||||
|
* @param outputStatics
|
||||||
|
* whether to include static fields
|
||||||
|
* @param excludeNullValues
|
||||||
|
* whether to exclude fields who value is null
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public <T> ReflectionToStringBuilder(
|
||||||
|
final T object, final ToStringStyle style, final StringBuffer buffer,
|
||||||
|
final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics,
|
||||||
|
final boolean excludeNullValues) {
|
||||||
|
super(checkNotNull(object), style, buffer);
|
||||||
|
this.setUpToClass(reflectUpToClass);
|
||||||
|
this.setAppendTransients(outputTransients);
|
||||||
|
this.setAppendStatics(outputStatics);
|
||||||
|
this.setExcludeNullValues(excludeNullValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not to append the given {@code Field}.
|
||||||
|
* <ul>
|
||||||
|
* <li>Transient fields are appended only if {@link #isAppendTransients()} returns {@code true}.
|
||||||
|
* <li>Static fields are appended only if {@link #isAppendStatics()} returns {@code true}.
|
||||||
|
* <li>Inner class fields are not appended.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* The Field to test.
|
||||||
|
* @return Whether or not to append the given {@code Field}.
|
||||||
|
*/
|
||||||
|
protected boolean accept(final Field field) {
|
||||||
|
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
|
||||||
|
// Reject field from inner class.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) {
|
||||||
|
// Reject transient fields.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) {
|
||||||
|
// Reject static fields.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.excludeFieldNames != null
|
||||||
|
&& Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
|
||||||
|
// Reject fields from the getExcludeFieldNames list.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !field.isAnnotationPresent(ToStringExclude.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Appends the fields and values defined by the given object of the given Class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If a cycle is detected as an object is "toString()'ed", such an object is rendered as if
|
||||||
|
* {@code Object.toString()} had been called and not implemented by the object.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param clazz
|
||||||
|
* The class of object parameter
|
||||||
|
*/
|
||||||
|
protected void appendFieldsIn(final Class<?> clazz) {
|
||||||
|
if (clazz.isArray()) {
|
||||||
|
this.reflectionAppendArray(this.getObject());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The elements in the returned array are not sorted and are not in any particular order.
|
||||||
|
final Field[] fields = ArraySorter.sort(clazz.getDeclaredFields(), Comparator.comparing(Field::getName));
|
||||||
|
AccessibleObject.setAccessible(fields, true);
|
||||||
|
for (final Field field : fields) {
|
||||||
|
final String fieldName = field.getName();
|
||||||
|
if (this.accept(field)) {
|
||||||
|
try {
|
||||||
|
// Warning: Field.get(Object) creates wrappers objects
|
||||||
|
// for primitive types.
|
||||||
|
final Object fieldValue = this.getValue(field);
|
||||||
|
if (!excludeNullValues || fieldValue != null) {
|
||||||
|
this.append(fieldName, fieldValue, !field.isAnnotationPresent(ToStringSummary.class));
|
||||||
|
}
|
||||||
|
} catch (final IllegalAccessException ex) {
|
||||||
|
//this can't happen. Would get a Security exception
|
||||||
|
// instead
|
||||||
|
//throw a runtime exception in case the impossible
|
||||||
|
// happens.
|
||||||
|
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the excludeFieldNames.
|
||||||
|
*/
|
||||||
|
public String[] getExcludeFieldNames() {
|
||||||
|
return this.excludeFieldNames.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Gets the last super class to stop appending fields for.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return The last super class to stop appending fields for.
|
||||||
|
*/
|
||||||
|
public Class<?> getUpToClass() {
|
||||||
|
return this.upToClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Calls {@code java.lang.reflect.Field.get(Object)}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* The Field to query.
|
||||||
|
* @return The Object from the given Field.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* see {@link java.lang.reflect.Field#get(Object)}
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
* see {@link java.lang.reflect.Field#get(Object)}
|
||||||
|
*
|
||||||
|
* @see java.lang.reflect.Field#get(Object)
|
||||||
|
*/
|
||||||
|
protected Object getValue(final Field field) throws IllegalAccessException {
|
||||||
|
return field.get(this.getObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Gets whether or not to append static fields.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Whether or not to append static fields.
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public boolean isAppendStatics() {
|
||||||
|
return this.appendStatics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Gets whether or not to append transient fields.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Whether or not to append transient fields.
|
||||||
|
*/
|
||||||
|
public boolean isAppendTransients() {
|
||||||
|
return this.appendTransients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Gets whether or not to append fields whose values are null.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Whether or not to append fields whose values are null.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public boolean isExcludeNullValues() {
|
||||||
|
return this.excludeNullValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Append to the {@code toString} an {@code Object} array.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* the array to add to the {@code toString}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public ReflectionToStringBuilder reflectionAppendArray(final Object array) {
|
||||||
|
this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sets whether or not to append static fields.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param appendStatics
|
||||||
|
* Whether or not to append static fields.
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public void setAppendStatics(final boolean appendStatics) {
|
||||||
|
this.appendStatics = appendStatics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sets whether or not to append transient fields.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param appendTransients
|
||||||
|
* Whether or not to append transient fields.
|
||||||
|
*/
|
||||||
|
public void setAppendTransients(final boolean appendTransients) {
|
||||||
|
this.appendTransients = appendTransients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sets whether or not to append fields whose values are null.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param excludeNullValues
|
||||||
|
* Whether or not to append fields whose values are null.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public void setExcludeNullValues(final boolean excludeNullValues) {
|
||||||
|
this.excludeNullValues = excludeNullValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the field names to exclude.
|
||||||
|
*
|
||||||
|
* @param excludeFieldNamesParam
|
||||||
|
* The excludeFieldNames to excluding from toString or {@code null}.
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public ReflectionToStringBuilder setExcludeFieldNames(final String... excludeFieldNamesParam) {
|
||||||
|
if (excludeFieldNamesParam == null) {
|
||||||
|
this.excludeFieldNames = null;
|
||||||
|
} else {
|
||||||
|
//clone and remove nulls
|
||||||
|
this.excludeFieldNames = ArraySorter.sort(toNoNullStringArray(excludeFieldNamesParam));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sets the last super class to stop appending fields for.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param clazz
|
||||||
|
* The last super class to stop appending fields for.
|
||||||
|
*/
|
||||||
|
public void setUpToClass(final Class<?> clazz) {
|
||||||
|
if (clazz != null) {
|
||||||
|
final Object object = getObject();
|
||||||
|
if (object != null && !clazz.isInstance(object)) {
|
||||||
|
throw new IllegalArgumentException("Specified class is not a superclass of the object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.upToClass = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Gets the String built by this builder.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the built string
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (this.getObject() == null) {
|
||||||
|
return this.getStyle().getNullText();
|
||||||
|
}
|
||||||
|
Class<?> clazz = this.getObject().getClass();
|
||||||
|
this.appendFieldsIn(clazz);
|
||||||
|
while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
this.appendFieldsIn(clazz);
|
||||||
|
}
|
||||||
|
return super.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,558 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Works with {@link ToStringBuilder} to create a {@code toString}.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is intended to be used as a singleton.
|
||||||
|
* There is no need to instantiate a new style each time.
|
||||||
|
* Simply instantiate the class once, customize the values as required, and
|
||||||
|
* store the result in a public static final variable for the rest of the
|
||||||
|
* program to access.</p>
|
||||||
|
*
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class StandardToStringStyle extends ToStringStyle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support.
|
||||||
|
*
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructor.</p>
|
||||||
|
*/
|
||||||
|
public StandardToStringStyle() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether to use the class name.</p>
|
||||||
|
*
|
||||||
|
* @return the current useClassName flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isUseClassName() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isUseClassName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether to use the class name.</p>
|
||||||
|
*
|
||||||
|
* @param useClassName the new useClassName flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUseClassName(final boolean useClassName) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setUseClassName(useClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether to output short or long class names.</p>
|
||||||
|
*
|
||||||
|
* @return the current useShortClassName flag
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isUseShortClassName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether to output short or long class names.</p>
|
||||||
|
*
|
||||||
|
* @param useShortClassName the new useShortClassName flag
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUseShortClassName(final boolean useShortClassName) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setUseShortClassName(useShortClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether to use the identity hash code.</p>
|
||||||
|
* @return the current useIdentityHashCode flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isUseIdentityHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether to use the identity hash code.</p>
|
||||||
|
*
|
||||||
|
* @param useIdentityHashCode the new useIdentityHashCode flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUseIdentityHashCode(final boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setUseIdentityHashCode(useIdentityHashCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether to use the field names passed in.</p>
|
||||||
|
*
|
||||||
|
* @return the current useFieldNames flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isUseFieldNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether to use the field names passed in.</p>
|
||||||
|
*
|
||||||
|
* @param useFieldNames the new useFieldNames flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUseFieldNames(final boolean useFieldNames) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setUseFieldNames(useFieldNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether to use full detail when the caller doesn't
|
||||||
|
* specify.</p>
|
||||||
|
*
|
||||||
|
* @return the current defaultFullDetail flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isDefaultFullDetail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether to use full detail when the caller doesn't
|
||||||
|
* specify.</p>
|
||||||
|
*
|
||||||
|
* @param defaultFullDetail the new defaultFullDetail flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setDefaultFullDetail(final boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setDefaultFullDetail(defaultFullDetail);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether to output array content detail.</p>
|
||||||
|
*
|
||||||
|
* @return the current array content detail setting
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isArrayContentDetail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether to output array content detail.</p>
|
||||||
|
*
|
||||||
|
* @param arrayContentDetail the new arrayContentDetail flag
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setArrayContentDetail(final boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setArrayContentDetail(arrayContentDetail);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the array start text.</p>
|
||||||
|
*
|
||||||
|
* @return the current array start text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getArrayStart() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getArrayStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the array start text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param arrayStart the new array start text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setArrayStart(final String arrayStart) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setArrayStart(arrayStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the array end text.</p>
|
||||||
|
*
|
||||||
|
* @return the current array end text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getArrayEnd() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getArrayEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the array end text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param arrayEnd the new array end text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setArrayEnd(final String arrayEnd) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setArrayEnd(arrayEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the array separator text.</p>
|
||||||
|
*
|
||||||
|
* @return the current array separator text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getArraySeparator() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getArraySeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the array separator text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param arraySeparator the new array separator text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setArraySeparator(final String arraySeparator) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setArraySeparator(arraySeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the content start text.</p>
|
||||||
|
*
|
||||||
|
* @return the current content start text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getContentStart() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getContentStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the content start text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param contentStart the new content start text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setContentStart(final String contentStart) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setContentStart(contentStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the content end text.</p>
|
||||||
|
*
|
||||||
|
* @return the current content end text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getContentEnd() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getContentEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the content end text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param contentEnd the new content end text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setContentEnd(final String contentEnd) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setContentEnd(contentEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the field name value separator text.</p>
|
||||||
|
*
|
||||||
|
* @return the current field name value separator text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getFieldNameValueSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the field name value separator text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param fieldNameValueSeparator the new field name value separator text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setFieldNameValueSeparator(final String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setFieldNameValueSeparator(fieldNameValueSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the field separator text.</p>
|
||||||
|
*
|
||||||
|
* @return the current field separator text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getFieldSeparator() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getFieldSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the field separator text.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param fieldSeparator the new field separator text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setFieldSeparator(final String fieldSeparator) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setFieldSeparator(fieldSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether the field separator should be added at the start
|
||||||
|
* of each buffer.</p>
|
||||||
|
*
|
||||||
|
* @return the fieldSeparatorAtStart flag
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isFieldSeparatorAtStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether the field separator should be added at the start
|
||||||
|
* of each buffer.</p>
|
||||||
|
*
|
||||||
|
* @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets whether the field separator should be added at the end
|
||||||
|
* of each buffer.</p>
|
||||||
|
*
|
||||||
|
* @return fieldSeparatorAtEnd flag
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.isFieldSeparatorAtEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets whether the field separator should be added at the end
|
||||||
|
* of each buffer.</p>
|
||||||
|
*
|
||||||
|
* @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the text to output when {@code null} found.</p>
|
||||||
|
*
|
||||||
|
* @return the current text to output when {@code null} found
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getNullText() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getNullText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the text to output when {@code null} found.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param nullText the new text to output when {@code null} found
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setNullText(final String nullText) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setNullText(nullText);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the text to output when a {@code Collection},
|
||||||
|
* {@code Map} or {@code Array} size is output.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output before the size value.</p>
|
||||||
|
*
|
||||||
|
* @return the current start of size text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSizeStartText() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getSizeStartText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the start text to output when a {@code Collection},
|
||||||
|
* {@code Map} or {@code Array} size is output.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output before the size value.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted to
|
||||||
|
* an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param sizeStartText the new start of size text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSizeStartText(final String sizeStartText) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setSizeStartText(sizeStartText);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the end text to output when a {@code Collection},
|
||||||
|
* {@code Map} or {@code Array} size is output.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output after the size value.</p>
|
||||||
|
*
|
||||||
|
* @return the current end of size text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSizeEndText() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getSizeEndText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the end text to output when a {@code Collection},
|
||||||
|
* {@code Map} or {@code Array} size is output.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output after the size value.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted
|
||||||
|
* to an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param sizeEndText the new end of size text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSizeEndText(final String sizeEndText) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setSizeEndText(sizeEndText);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the start text to output when an {@code Object} is
|
||||||
|
* output in summary mode.</p>
|
||||||
|
*
|
||||||
|
* <P>This is output before the size value.</p>
|
||||||
|
*
|
||||||
|
* @return the current start of summary text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getSummaryObjectStartText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the start text to output when an {@code Object} is
|
||||||
|
* output in summary mode.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output before the size value.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted to
|
||||||
|
* an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param summaryObjectStartText the new start of summary text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSummaryObjectStartText(final String summaryObjectStartText) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setSummaryObjectStartText(summaryObjectStartText);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the end text to output when an {@code Object} is
|
||||||
|
* output in summary mode.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output after the size value.</p>
|
||||||
|
*
|
||||||
|
* @return the current end of summary text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class
|
||||||
|
return super.getSummaryObjectEndText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the end text to output when an {@code Object} is
|
||||||
|
* output in summary mode.</p>
|
||||||
|
*
|
||||||
|
* <p>This is output after the size value.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code null} is accepted, but will be converted to
|
||||||
|
* an empty String.</p>
|
||||||
|
*
|
||||||
|
* @param summaryObjectEndText the new end of summary text
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSummaryObjectEndText(final String summaryObjectEndText) { // NOPMD as this is implementing the abstract class
|
||||||
|
super.setSummaryObjectEndText(summaryObjectEndText);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
35
after/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java
Executable file
35
after/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this annotation to exclude a field from being used by
|
||||||
|
* the {@link ReflectionToStringBuilder}.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface ToStringExclude {
|
||||||
|
// empty
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.builder;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this annotation on the fields to get the summary instead of the detailed
|
||||||
|
* information when using {@link ReflectionToStringBuilder}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Notice that not all {@link ToStringStyle} implementations support the
|
||||||
|
* appendSummary method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.8
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface ToStringSummary {
|
||||||
|
// empty
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* <p>Assists in creating consistent {@code equals(Object)}, {@code toString()}, {@code hashCode()}, and {@code compareTo(Object)} methods.
|
||||||
|
* These classes are not thread-safe.</p>
|
||||||
|
*
|
||||||
|
* <p>When you write a {@link java.lang.Object#hashCode() hashCode()}, do you check Bloch's Effective Java? No?
|
||||||
|
* You just hack in a quick number?
|
||||||
|
* Well {@link org.apache.commons.lang3.builder.HashCodeBuilder} will save your day.
|
||||||
|
* It, and its buddies ({@link org.apache.commons.lang3.builder.EqualsBuilder}, {@link org.apache.commons.lang3.builder.CompareToBuilder}, {@link org.apache.commons.lang3.builder.ToStringBuilder}), take care of the nasty bits while you focus on the important bits, like which fields will go into making up the hashcode.</p>
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#equals(Object)
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
* @see java.lang.Object#hashCode()
|
||||||
|
* @see java.lang.Comparable#compareTo(Object)
|
||||||
|
*
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.builder;
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.compare;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Utility library to provide helper methods for translating {@link Comparable#compareTo} result into a boolean.</p>
|
||||||
|
*
|
||||||
|
* <p>Example: {@code boolean x = is(myComparable).lessThanOrEqualTo(otherComparable)}</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
*
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public class ComparableUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the available methods
|
||||||
|
*
|
||||||
|
* @param <A> the type of objects that this object may be compared against.
|
||||||
|
*/
|
||||||
|
public static class ComparableCheckBuilder<A extends Comparable<A>> {
|
||||||
|
|
||||||
|
private final A a;
|
||||||
|
|
||||||
|
private ComparableCheckBuilder(final A a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} is object passed to {@link #is}.
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @param c the object to compare to the base object
|
||||||
|
* @return true if the base object is between b and c
|
||||||
|
*/
|
||||||
|
public boolean between(final A b, final A c) {
|
||||||
|
return betweenOrdered(b, c) || betweenOrdered(c, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is object passed to {@link #is}.
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @param c the object to compare to the base object
|
||||||
|
* @return true if the base object is between b and c and not equal to those
|
||||||
|
*/
|
||||||
|
public boolean betweenExclusive(final A b, final A c) {
|
||||||
|
return betweenOrderedExclusive(b, c) || betweenOrderedExclusive(c, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean betweenOrdered(final A b, final A c) {
|
||||||
|
return greaterThanOrEqualTo(b) && lessThanOrEqualTo(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean betweenOrderedExclusive(final A b, final A c) {
|
||||||
|
return greaterThan(b) && lessThan(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the object passed to {@link #is} is equal to {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @return true if the value returned by {@link Comparable#compareTo} is equal to {@code 0}
|
||||||
|
*/
|
||||||
|
public boolean equalTo(final A b) {
|
||||||
|
return a.compareTo(b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the object passed to {@link #is} is greater than {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @return true if the value returned by {@link Comparable#compareTo} is greater than {@code 0}
|
||||||
|
*/
|
||||||
|
public boolean greaterThan(final A b) {
|
||||||
|
return a.compareTo(b) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the object passed to {@link #is} is greater than or equal to {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @return true if the value returned by {@link Comparable#compareTo} is greater than or equal to {@code 0}
|
||||||
|
*/
|
||||||
|
public boolean greaterThanOrEqualTo(final A b) {
|
||||||
|
return a.compareTo(b) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the object passed to {@link #is} is less than {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @return true if the value returned by {@link Comparable#compareTo} is less than {@code 0}
|
||||||
|
*/
|
||||||
|
public boolean lessThan(final A b) {
|
||||||
|
return a.compareTo(b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the object passed to {@link #is} is less than or equal to {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the base object
|
||||||
|
* @return true if the value returned by {@link Comparable#compareTo} is less than or equal to {@code 0}
|
||||||
|
*/
|
||||||
|
public boolean lessThanOrEqualTo(final A b) {
|
||||||
|
return a.compareTo(b) <= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} is the tested object.
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the tested object
|
||||||
|
* @param c the object to compare to the tested object
|
||||||
|
* @param <A> type of the test object
|
||||||
|
* @return a predicate for true if the tested object is between b and c
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> Predicate<A> between(final A b, final A c) {
|
||||||
|
return a -> is(a).between(b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is the tested object.
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the tested object
|
||||||
|
* @param c the object to compare to the tested object
|
||||||
|
* @param <A> type of the test object
|
||||||
|
* @return a predicate for true if the tested object is between b and c and not equal to those
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> Predicate<A> betweenExclusive(final A b, final A c) {
|
||||||
|
return a -> is(a).betweenExclusive(b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the tested object is greater than or equal to {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the tested object
|
||||||
|
* @param <A> type of the test object
|
||||||
|
* @return a predicate for true if the value returned by {@link Comparable#compareTo}
|
||||||
|
* is greater than or equal to {@code 0}
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> Predicate<A> ge(final A b) {
|
||||||
|
return a -> is(a).greaterThanOrEqualTo(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the tested object is greater than {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the tested object
|
||||||
|
* @param <A> type of the test object
|
||||||
|
* @return a predicate for true if the value returned by {@link Comparable#compareTo} is greater than {@code 0}
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> Predicate<A> gt(final A b) {
|
||||||
|
return a -> is(a).greaterThan(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the available methods
|
||||||
|
*
|
||||||
|
* @param a base object in the further comparison
|
||||||
|
* @param <A> type of the base object
|
||||||
|
* @return a builder object with further methods
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> ComparableCheckBuilder<A> is(final A a) {
|
||||||
|
return new ComparableCheckBuilder<>(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the tested object is less than or equal to {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the tested object
|
||||||
|
* @param <A> type of the test object
|
||||||
|
* @return a predicate for true if the value returned by {@link Comparable#compareTo}
|
||||||
|
* is less than or equal to {@code 0}
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> Predicate<A> le(final A b) {
|
||||||
|
return a -> is(a).lessThanOrEqualTo(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the tested object is less than {@code b}
|
||||||
|
*
|
||||||
|
* @param b the object to compare to the tested object
|
||||||
|
* @param <A> type of the test object
|
||||||
|
* @return a predicate for true if the value returned by {@link Comparable#compareTo} is less than {@code 0}
|
||||||
|
*/
|
||||||
|
public static <A extends Comparable<A>> Predicate<A> lt(final A b) {
|
||||||
|
return a -> is(a).lessThan(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComparableUtils() {}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.lang3.compare;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares Object's {@link Object#toString()} values.
|
||||||
|
*
|
||||||
|
* This class is stateless.
|
||||||
|
*
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
public final class ObjectToStringComparator implements Comparator<Object>, Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton instance.
|
||||||
|
*
|
||||||
|
* This class is stateless.
|
||||||
|
*/
|
||||||
|
public static final ObjectToStringComparator INSTANCE = new ObjectToStringComparator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For {@link Serializable}.
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(final Object o1, final Object o2) {
|
||||||
|
if (o1 == null && o2 == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (o1 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (o2 == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
final String string1 = o1.toString();
|
||||||
|
final String string2 = o2.toString();
|
||||||
|
// No guarantee that toString() returns a non-null value, despite what Spotbugs thinks.
|
||||||
|
if (string1 == null && string2 == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (string1 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (string2 == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return string1.compareTo(string2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides classes to work with the {@link java.lang.Comparable} and {@link java.util.Comparator} interfaces.
|
||||||
|
*
|
||||||
|
* @since 3.10
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.compare;
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.beans.PropertyChangeSupport;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for circuit breakers.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the value monitored by this circuit breaker
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public abstract class AbstractCircuitBreaker<T> implements CircuitBreaker<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the <em>open</em> property as it is passed to registered
|
||||||
|
* change listeners.
|
||||||
|
*/
|
||||||
|
public static final String PROPERTY_NAME = "open";
|
||||||
|
|
||||||
|
/** The current state of this circuit breaker. */
|
||||||
|
protected final AtomicReference<State> state = new AtomicReference<>(State.CLOSED);
|
||||||
|
|
||||||
|
/** An object for managing change listeners registered at this instance. */
|
||||||
|
private final PropertyChangeSupport changeSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@code AbstractCircuitBreaker}. It also creates an internal {@code PropertyChangeSupport}.
|
||||||
|
*/
|
||||||
|
public AbstractCircuitBreaker() {
|
||||||
|
changeSupport = new PropertyChangeSupport(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return isOpen(state.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return !isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract boolean checkState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract boolean incrementAndCheckState(T increment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
changeState(State.CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void open() {
|
||||||
|
changeState(State.OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given state value to a boolean <em>open</em> property.
|
||||||
|
*
|
||||||
|
* @param state the state to be converted
|
||||||
|
* @return the boolean open flag
|
||||||
|
*/
|
||||||
|
protected static boolean isOpen(final State state) {
|
||||||
|
return state == State.OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the internal state of this circuit breaker. If there is actually a change
|
||||||
|
* of the state value, all registered change listeners are notified.
|
||||||
|
*
|
||||||
|
* @param newState the new state to be set
|
||||||
|
*/
|
||||||
|
protected void changeState(final State newState) {
|
||||||
|
if (state.compareAndSet(newState.oppositeState(), newState)) {
|
||||||
|
changeSupport.firePropertyChange(PROPERTY_NAME, !isOpen(newState), isOpen(newState));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a change listener to this circuit breaker. This listener is notified whenever
|
||||||
|
* the state of this circuit breaker changes. If the listener is
|
||||||
|
* <strong>null</strong>, it is silently ignored.
|
||||||
|
*
|
||||||
|
* @param listener the listener to be added
|
||||||
|
*/
|
||||||
|
public void addChangeListener(final PropertyChangeListener listener) {
|
||||||
|
changeSupport.addPropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified change listener from this circuit breaker.
|
||||||
|
*
|
||||||
|
* @param listener the listener to be removed
|
||||||
|
*/
|
||||||
|
public void removeChangeListener(final PropertyChangeListener listener) {
|
||||||
|
changeSupport.removePropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal enumeration representing the different states of a circuit
|
||||||
|
* breaker. This class also contains some logic for performing state
|
||||||
|
* transitions. This is done to avoid complex if-conditions in the code of
|
||||||
|
* {@code CircuitBreaker}.
|
||||||
|
*/
|
||||||
|
protected enum State {
|
||||||
|
|
||||||
|
/** The closed state. */
|
||||||
|
CLOSED {
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public State oppositeState() {
|
||||||
|
return OPEN;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** The open state. */
|
||||||
|
OPEN {
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public State oppositeState() {
|
||||||
|
return CLOSED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the opposite state to the represented state. This is useful
|
||||||
|
* for flipping the current state.
|
||||||
|
*
|
||||||
|
* @return the opposite state
|
||||||
|
*/
|
||||||
|
public abstract State oppositeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A specialized implementation of the {@code ConcurrentInitializer} interface
|
||||||
|
* based on an {@link AtomicReference} variable.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This class maintains a member field of type {@code AtomicReference}. It
|
||||||
|
* implements the following algorithm to create and initialize an object in its
|
||||||
|
* {@link #get()} method:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>First it is checked whether the {@code AtomicReference} variable contains
|
||||||
|
* already a value. If this is the case, the value is directly returned.</li>
|
||||||
|
* <li>Otherwise the {@link #initialize()} method is called. This method must be
|
||||||
|
* defined in concrete subclasses to actually create the managed object.</li>
|
||||||
|
* <li>After the object was created by {@link #initialize()} it is checked
|
||||||
|
* whether the {@code AtomicReference} variable is still undefined. This has to
|
||||||
|
* be done because in the meantime another thread may have initialized the
|
||||||
|
* object. If the reference is still empty, the newly created object is stored
|
||||||
|
* in it and returned by this method.</li>
|
||||||
|
* <li>Otherwise the value stored in the {@code AtomicReference} is returned.</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Because atomic variables are used this class does not need any
|
||||||
|
* synchronization. So there is no danger of deadlock, and access to the managed
|
||||||
|
* object is efficient. However, if multiple threads access the {@code
|
||||||
|
* AtomicInitializer} object before it has been initialized almost at the same
|
||||||
|
* time, it can happen that {@link #initialize()} is called multiple times. The
|
||||||
|
* algorithm outlined above guarantees that {@link #get()} always returns the
|
||||||
|
* same object though.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Compared with the {@link LazyInitializer} class, this class can be more
|
||||||
|
* efficient because it does not need synchronization. The drawback is that the
|
||||||
|
* {@link #initialize()} method can be called multiple times which may be
|
||||||
|
* problematic if the creation of the managed object is expensive. As a rule of
|
||||||
|
* thumb this initializer implementation is preferable if there are not too many
|
||||||
|
* threads involved and the probability that multiple threads access an
|
||||||
|
* uninitialized object is small. If there is high parallelism,
|
||||||
|
* {@link LazyInitializer} is more appropriate.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
* @param <T> the type of the object managed by this initializer class
|
||||||
|
*/
|
||||||
|
public abstract class AtomicInitializer<T> implements ConcurrentInitializer<T> {
|
||||||
|
/** Holds the reference to the managed object. */
|
||||||
|
private final AtomicReference<T> reference = new AtomicReference<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object managed by this initializer. The object is created if
|
||||||
|
* it is not available yet and stored internally. This method always returns
|
||||||
|
* the same object.
|
||||||
|
*
|
||||||
|
* @return the object created by this {@code AtomicInitializer}
|
||||||
|
* @throws ConcurrentException if an error occurred during initialization of
|
||||||
|
* the object
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T get() throws ConcurrentException {
|
||||||
|
T result = reference.get();
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = initialize();
|
||||||
|
if (!reference.compareAndSet(null, result)) {
|
||||||
|
// another thread has initialized the reference
|
||||||
|
result = reference.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and initializes the object managed by this {@code
|
||||||
|
* AtomicInitializer}. This method is called by {@link #get()} when the
|
||||||
|
* managed object is not available yet. An implementation can focus on the
|
||||||
|
* creation of the object. No synchronization is needed, as this is already
|
||||||
|
* handled by {@code get()}. As stated by the class comment, it is possible
|
||||||
|
* that this method is called multiple times.
|
||||||
|
*
|
||||||
|
* @return the managed data object
|
||||||
|
* @throws ConcurrentException if an error occurs during object creation
|
||||||
|
*/
|
||||||
|
protected abstract T initialize() throws ConcurrentException;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A specialized {@code ConcurrentInitializer} implementation which is similar
|
||||||
|
* to {@link AtomicInitializer}, but ensures that the {@link #initialize()}
|
||||||
|
* method is called only once.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* As {@link AtomicInitializer} this class is based on atomic variables, so it
|
||||||
|
* can create an object under concurrent access without synchronization.
|
||||||
|
* However, it implements an additional check to guarantee that the
|
||||||
|
* {@link #initialize()} method which actually creates the object cannot be
|
||||||
|
* called multiple times.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Because of this additional check this implementation is slightly less
|
||||||
|
* efficient than {@link AtomicInitializer}, but if the object creation in the
|
||||||
|
* {@code initialize()} method is expensive or if multiple invocations of
|
||||||
|
* {@code initialize()} are problematic, it is the better alternative.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* From its semantics this class has the same properties as
|
||||||
|
* {@link LazyInitializer}. It is a "save" implementation of the lazy
|
||||||
|
* initializer pattern. Comparing both classes in terms of efficiency is
|
||||||
|
* difficult because which one is faster depends on multiple factors. Because
|
||||||
|
* {@code AtomicSafeInitializer} does not use synchronization at all it probably
|
||||||
|
* outruns {@link LazyInitializer}, at least under low or moderate concurrent
|
||||||
|
* access. Developers should run their own benchmarks on the expected target
|
||||||
|
* platform to decide which implementation is suitable for their specific use
|
||||||
|
* case.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
* @param <T> the type of the object managed by this initializer class
|
||||||
|
*/
|
||||||
|
public abstract class AtomicSafeInitializer<T> implements
|
||||||
|
ConcurrentInitializer<T> {
|
||||||
|
/** A guard which ensures that initialize() is called only once. */
|
||||||
|
private final AtomicReference<AtomicSafeInitializer<T>> factory =
|
||||||
|
new AtomicReference<>();
|
||||||
|
|
||||||
|
/** Holds the reference to the managed object. */
|
||||||
|
private final AtomicReference<T> reference = new AtomicReference<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets (and initialize, if not initialized yet) the required object
|
||||||
|
*
|
||||||
|
* @return lazily initialized object
|
||||||
|
* @throws ConcurrentException if the initialization of the object causes an
|
||||||
|
* exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final T get() throws ConcurrentException {
|
||||||
|
T result;
|
||||||
|
|
||||||
|
while ((result = reference.get()) == null) {
|
||||||
|
if (factory.compareAndSet(null, this)) {
|
||||||
|
reference.set(initialize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and initializes the object managed by this
|
||||||
|
* {@code AtomicInitializer}. This method is called by {@link #get()} when
|
||||||
|
* the managed object is not available yet. An implementation can focus on
|
||||||
|
* the creation of the object. No synchronization is needed, as this is
|
||||||
|
* already handled by {@code get()}. This method is guaranteed to be called
|
||||||
|
* only once.
|
||||||
|
*
|
||||||
|
* @return the managed data object
|
||||||
|
* @throws ConcurrentException if an error occurs during object creation
|
||||||
|
*/
|
||||||
|
protected abstract T initialize() throws ConcurrentException;
|
||||||
|
}
|
|
@ -0,0 +1,334 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A class that allows complex initialization operations in a background task.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Applications often have to do some expensive initialization steps when they
|
||||||
|
* are started, e.g. constructing a connection to a database, reading a
|
||||||
|
* configuration file, etc. Doing these things in parallel can enhance
|
||||||
|
* performance as the CPU load can be improved. However, when access to the
|
||||||
|
* resources initialized in a background thread is actually required,
|
||||||
|
* synchronization has to be performed to ensure that their initialization is
|
||||||
|
* complete.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This abstract base class provides support for this use case. A concrete
|
||||||
|
* subclass must implement the {@link #initialize()} method. Here an arbitrary
|
||||||
|
* initialization can be implemented, and a result object can be returned. With
|
||||||
|
* this method in place the basic usage of this class is as follows (where
|
||||||
|
* {@code MyBackgroundInitializer} is a concrete subclass):
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* MyBackgroundInitializer initializer = new MyBackgroundInitializer();
|
||||||
|
* initializer.start();
|
||||||
|
* // Now do some other things. Initialization runs in a parallel thread
|
||||||
|
* ...
|
||||||
|
* // Wait for the end of initialization and access the result object
|
||||||
|
* Object result = initializer.get();
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* After the construction of a {@code BackgroundInitializer} object its
|
||||||
|
* {@link #start()} method has to be called. This starts the background
|
||||||
|
* processing. The application can now continue to do other things. When it
|
||||||
|
* needs access to the object produced by the {@code BackgroundInitializer} it
|
||||||
|
* calls its {@link #get()} method. If initialization is already complete,
|
||||||
|
* {@link #get()} returns the result object immediately. Otherwise it blocks
|
||||||
|
* until the result object is fully constructed.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* {@code BackgroundInitializer} is a thin wrapper around a {@code Future}
|
||||||
|
* object and uses an {@code ExecutorService} for running the background
|
||||||
|
* initialization task. It is possible to pass in an {@code ExecutorService} at
|
||||||
|
* construction time or set one using {@code setExternalExecutor()} before
|
||||||
|
* {@code start()} was called. Then this object is used to spawn the background
|
||||||
|
* task. If no {@code ExecutorService} has been provided, {@code
|
||||||
|
* BackgroundInitializer} creates a temporary {@code ExecutorService} and
|
||||||
|
* destroys it when initialization is complete.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The methods provided by {@code BackgroundInitializer} provide for minimal
|
||||||
|
* interaction with the wrapped {@code Future} object. It is also possible to
|
||||||
|
* obtain the {@code Future} object directly. Then the enhanced functionality
|
||||||
|
* offered by {@code Future} can be used, e.g. to check whether the background
|
||||||
|
* operation is complete or to cancel the operation.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
* @param <T> the type of the object managed by this initializer class
|
||||||
|
*/
|
||||||
|
public abstract class BackgroundInitializer<T> implements
|
||||||
|
ConcurrentInitializer<T> {
|
||||||
|
/** The external executor service for executing tasks. */
|
||||||
|
private ExecutorService externalExecutor; // @GuardedBy("this")
|
||||||
|
|
||||||
|
/** A reference to the executor service that is actually used. */
|
||||||
|
private ExecutorService executor; // @GuardedBy("this")
|
||||||
|
|
||||||
|
/** Stores the handle to the background task. */
|
||||||
|
private Future<T> future; // @GuardedBy("this")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code BackgroundInitializer}. No external
|
||||||
|
* {@code ExecutorService} is used.
|
||||||
|
*/
|
||||||
|
protected BackgroundInitializer() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code BackgroundInitializer} and initializes
|
||||||
|
* it with the given {@code ExecutorService}. If the {@code ExecutorService}
|
||||||
|
* is not null, the background task for initializing this object will be
|
||||||
|
* scheduled at this service. Otherwise a new temporary {@code
|
||||||
|
* ExecutorService} is created.
|
||||||
|
*
|
||||||
|
* @param exec an external {@code ExecutorService} to be used for task
|
||||||
|
* execution
|
||||||
|
*/
|
||||||
|
protected BackgroundInitializer(final ExecutorService exec) {
|
||||||
|
setExternalExecutor(exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the external {@code ExecutorService} to be used by this class.
|
||||||
|
*
|
||||||
|
* @return the {@code ExecutorService}
|
||||||
|
*/
|
||||||
|
public final synchronized ExecutorService getExternalExecutor() {
|
||||||
|
return externalExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a flag whether this {@code BackgroundInitializer} has already
|
||||||
|
* been started.
|
||||||
|
*
|
||||||
|
* @return a flag whether the {@link #start()} method has already been
|
||||||
|
* called
|
||||||
|
*/
|
||||||
|
public synchronized boolean isStarted() {
|
||||||
|
return future != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an {@code ExecutorService} to be used by this class. The {@code
|
||||||
|
* ExecutorService} passed to this method is used for executing the
|
||||||
|
* background task. Thus it is possible to re-use an already existing
|
||||||
|
* {@code ExecutorService} or to use a specially configured one. If no
|
||||||
|
* {@code ExecutorService} is set, this instance creates a temporary one and
|
||||||
|
* destroys it after background initialization is complete. Note that this
|
||||||
|
* method must be called before {@link #start()}; otherwise an exception is
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @param externalExecutor the {@code ExecutorService} to be used
|
||||||
|
* @throws IllegalStateException if this initializer has already been
|
||||||
|
* started
|
||||||
|
*/
|
||||||
|
public final synchronized void setExternalExecutor(
|
||||||
|
final ExecutorService externalExecutor) {
|
||||||
|
if (isStarted()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot set ExecutorService after start()!");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.externalExecutor = externalExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the background initialization. With this method the initializer
|
||||||
|
* becomes active and invokes the {@link #initialize()} method in a
|
||||||
|
* background task. A {@code BackgroundInitializer} can be started exactly
|
||||||
|
* once. The return value of this method determines whether the start was
|
||||||
|
* successful: only the first invocation of this method returns <b>true</b>,
|
||||||
|
* following invocations will return <b>false</b>.
|
||||||
|
*
|
||||||
|
* @return a flag whether the initializer could be started successfully
|
||||||
|
*/
|
||||||
|
public synchronized boolean start() {
|
||||||
|
// Not yet started?
|
||||||
|
if (!isStarted()) {
|
||||||
|
|
||||||
|
// Determine the executor to use and whether a temporary one has to
|
||||||
|
// be created
|
||||||
|
final ExecutorService tempExec;
|
||||||
|
executor = getExternalExecutor();
|
||||||
|
if (executor == null) {
|
||||||
|
executor = tempExec = createExecutor();
|
||||||
|
} else {
|
||||||
|
tempExec = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
future = executor.submit(createTask(tempExec));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the result of the background initialization. This method blocks
|
||||||
|
* until initialization is complete. If the background processing caused a
|
||||||
|
* runtime exception, it is directly thrown by this method. Checked
|
||||||
|
* exceptions, including {@code InterruptedException} are wrapped in a
|
||||||
|
* {@link ConcurrentException}. Calling this method before {@link #start()}
|
||||||
|
* was called causes an {@code IllegalStateException} exception to be
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @return the object produced by this initializer
|
||||||
|
* @throws ConcurrentException if a checked exception occurred during
|
||||||
|
* background processing
|
||||||
|
* @throws IllegalStateException if {@link #start()} has not been called
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T get() throws ConcurrentException {
|
||||||
|
try {
|
||||||
|
return getFuture().get();
|
||||||
|
} catch (final ExecutionException execex) {
|
||||||
|
ConcurrentUtils.handleCause(execex);
|
||||||
|
return null; // should not be reached
|
||||||
|
} catch (final InterruptedException iex) {
|
||||||
|
// reset interrupted state
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new ConcurrentException(iex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@code Future} object that was created when {@link #start()}
|
||||||
|
* was called. Therefore this method can only be called after {@code
|
||||||
|
* start()}.
|
||||||
|
*
|
||||||
|
* @return the {@code Future} object wrapped by this initializer
|
||||||
|
* @throws IllegalStateException if {@link #start()} has not been called
|
||||||
|
*/
|
||||||
|
public synchronized Future<T> getFuture() {
|
||||||
|
if (future == null) {
|
||||||
|
throw new IllegalStateException("start() must be called first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@code ExecutorService} that is actually used for executing
|
||||||
|
* the background task. This method can be called after {@link #start()}
|
||||||
|
* (before {@code start()} it returns <b>null</b>). If an external executor
|
||||||
|
* was set, this is also the active executor. Otherwise this method returns
|
||||||
|
* the temporary executor that was created by this object.
|
||||||
|
*
|
||||||
|
* @return the {@code ExecutorService} for executing the background task
|
||||||
|
*/
|
||||||
|
protected final synchronized ExecutorService getActiveExecutor() {
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of background tasks to be created for this
|
||||||
|
* initializer. This information is evaluated when a temporary {@code
|
||||||
|
* ExecutorService} is created. This base implementation returns 1. Derived
|
||||||
|
* classes that do more complex background processing can override it. This
|
||||||
|
* method is called from a synchronized block by the {@link #start()}
|
||||||
|
* method. Therefore overriding methods should be careful with obtaining
|
||||||
|
* other locks and return as fast as possible.
|
||||||
|
*
|
||||||
|
* @return the number of background tasks required by this initializer
|
||||||
|
*/
|
||||||
|
protected int getTaskCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the initialization. This method is called in a background task
|
||||||
|
* when this {@code BackgroundInitializer} is started. It must be
|
||||||
|
* implemented by a concrete subclass. An implementation is free to perform
|
||||||
|
* arbitrary initialization. The object returned by this method can be
|
||||||
|
* queried using the {@link #get()} method.
|
||||||
|
*
|
||||||
|
* @return a result object
|
||||||
|
* @throws Exception if an error occurs
|
||||||
|
*/
|
||||||
|
protected abstract T initialize() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a task for the background initialization. The {@code Callable}
|
||||||
|
* object returned by this method is passed to the {@code ExecutorService}.
|
||||||
|
* This implementation returns a task that invokes the {@link #initialize()}
|
||||||
|
* method. If a temporary {@code ExecutorService} is used, it is destroyed
|
||||||
|
* at the end of the task.
|
||||||
|
*
|
||||||
|
* @param execDestroy the {@code ExecutorService} to be destroyed by the
|
||||||
|
* task
|
||||||
|
* @return a task for the background initialization
|
||||||
|
*/
|
||||||
|
private Callable<T> createTask(final ExecutorService execDestroy) {
|
||||||
|
return new InitializationTask(execDestroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@code ExecutorService} to be used. This method is called if
|
||||||
|
* no {@code ExecutorService} was provided at construction time.
|
||||||
|
*
|
||||||
|
* @return the {@code ExecutorService} to be used
|
||||||
|
*/
|
||||||
|
private ExecutorService createExecutor() {
|
||||||
|
return Executors.newFixedThreadPool(getTaskCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InitializationTask implements Callable<T> {
|
||||||
|
/** Stores the executor service to be destroyed at the end. */
|
||||||
|
private final ExecutorService execFinally;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code InitializationTask} and initializes
|
||||||
|
* it with the {@code ExecutorService} to be destroyed at the end.
|
||||||
|
*
|
||||||
|
* @param exec the {@code ExecutorService}
|
||||||
|
*/
|
||||||
|
InitializationTask(final ExecutorService exec) {
|
||||||
|
execFinally = exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates initialization and returns the result.
|
||||||
|
*
|
||||||
|
* @return the result object
|
||||||
|
* @throws Exception if an error occurs
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T call() throws Exception {
|
||||||
|
try {
|
||||||
|
return initialize();
|
||||||
|
} finally {
|
||||||
|
if (execFinally != null) {
|
||||||
|
execFinally.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,374 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* An implementation of the {@code ThreadFactory} interface that provides some
|
||||||
|
* configuration options for the threads it creates.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* A {@code ThreadFactory} is used for instance by an {@code ExecutorService} to
|
||||||
|
* create the threads it uses for executing tasks. In many cases users do not
|
||||||
|
* have to care about a {@code ThreadFactory} because the default one used by an
|
||||||
|
* {@code ExecutorService} will do. However, if there are special requirements
|
||||||
|
* for the threads, a custom {@code ThreadFactory} has to be created.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This class provides some frequently needed configuration options for the
|
||||||
|
* threads it creates. These are the following:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>A name pattern for the threads created by this factory can be specified.
|
||||||
|
* This is often useful if an application uses multiple executor services for
|
||||||
|
* different purposes. If the names of the threads used by these services have
|
||||||
|
* meaningful names, log output or exception traces can be much easier to read.
|
||||||
|
* Naming patterns are <em>format strings</em> as used by the {@code
|
||||||
|
* String.format()} method. The string can contain the place holder {@code %d}
|
||||||
|
* which will be replaced by the number of the current thread ({@code
|
||||||
|
* ThreadFactoryImpl} keeps a counter of the threads it has already created).
|
||||||
|
* For instance, the naming pattern {@code "My %d. worker thread"} will result
|
||||||
|
* in thread names like {@code "My 1. worker thread"}, {@code
|
||||||
|
* "My 2. worker thread"} and so on.</li>
|
||||||
|
* <li>A flag whether the threads created by this factory should be daemon
|
||||||
|
* threads. This can impact the exit behavior of the current Java application
|
||||||
|
* because the JVM shuts down if there are only daemon threads running.</li>
|
||||||
|
* <li>The priority of the thread. Here an integer value can be provided. The
|
||||||
|
* {@code java.lang.Thread} class defines constants for valid ranges of priority
|
||||||
|
* values.</li>
|
||||||
|
* <li>The {@code UncaughtExceptionHandler} for the thread. This handler is
|
||||||
|
* called if an uncaught exception occurs within the thread.</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* {@code BasicThreadFactory} wraps another thread factory which actually
|
||||||
|
* creates new threads. The configuration options are set on the threads created
|
||||||
|
* by the wrapped thread factory. On construction time the factory to be wrapped
|
||||||
|
* can be specified. If none is provided, a default {@code ThreadFactory} is
|
||||||
|
* used.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Instances of {@code BasicThreadFactory} are not created directly, but the
|
||||||
|
* nested {@code Builder} class is used for this purpose. Using the builder only
|
||||||
|
* the configuration options an application is interested in need to be set. The
|
||||||
|
* following example shows how a {@code BasicThreadFactory} is created and
|
||||||
|
* installed in an {@code ExecutorService}:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* // Create a factory that produces daemon threads with a naming pattern and
|
||||||
|
* // a priority
|
||||||
|
* BasicThreadFactory factory = new BasicThreadFactory.Builder()
|
||||||
|
* .namingPattern("workerthread-%d")
|
||||||
|
* .daemon(true)
|
||||||
|
* .priority(Thread.MAX_PRIORITY)
|
||||||
|
* .build();
|
||||||
|
* // Create an executor service for single-threaded execution
|
||||||
|
* ExecutorService exec = Executors.newSingleThreadExecutor(factory);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class BasicThreadFactory implements ThreadFactory {
|
||||||
|
/** A counter for the threads created by this factory. */
|
||||||
|
private final AtomicLong threadCounter;
|
||||||
|
|
||||||
|
/** Stores the wrapped factory. */
|
||||||
|
private final ThreadFactory wrappedFactory;
|
||||||
|
|
||||||
|
/** Stores the uncaught exception handler. */
|
||||||
|
private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
||||||
|
|
||||||
|
/** Stores the naming pattern for newly created threads. */
|
||||||
|
private final String namingPattern;
|
||||||
|
|
||||||
|
/** Stores the priority. */
|
||||||
|
private final Integer priority;
|
||||||
|
|
||||||
|
/** Stores the daemon status flag. */
|
||||||
|
private final Boolean daemon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code ThreadFactoryImpl} and configures it
|
||||||
|
* from the specified {@code Builder} object.
|
||||||
|
*
|
||||||
|
* @param builder the {@code Builder} object
|
||||||
|
*/
|
||||||
|
private BasicThreadFactory(final Builder builder) {
|
||||||
|
if (builder.wrappedFactory == null) {
|
||||||
|
wrappedFactory = Executors.defaultThreadFactory();
|
||||||
|
} else {
|
||||||
|
wrappedFactory = builder.wrappedFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
namingPattern = builder.namingPattern;
|
||||||
|
priority = builder.priority;
|
||||||
|
daemon = builder.daemon;
|
||||||
|
uncaughtExceptionHandler = builder.exceptionHandler;
|
||||||
|
|
||||||
|
threadCounter = new AtomicLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the wrapped {@code ThreadFactory}. This factory is used for
|
||||||
|
* actually creating threads. This method never returns <b>null</b>. If no
|
||||||
|
* {@code ThreadFactory} was passed when this object was created, a default
|
||||||
|
* thread factory is returned.
|
||||||
|
*
|
||||||
|
* @return the wrapped {@code ThreadFactory}
|
||||||
|
*/
|
||||||
|
public final ThreadFactory getWrappedFactory() {
|
||||||
|
return wrappedFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the naming pattern for naming newly created threads. Result can
|
||||||
|
* be <b>null</b> if no naming pattern was provided.
|
||||||
|
*
|
||||||
|
* @return the naming pattern
|
||||||
|
*/
|
||||||
|
public final String getNamingPattern() {
|
||||||
|
return namingPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the daemon flag. This flag determines whether newly created
|
||||||
|
* threads should be daemon threads. If <b>true</b>, this factory object
|
||||||
|
* calls {@code setDaemon(true)} on the newly created threads. Result can be
|
||||||
|
* <b>null</b> if no daemon flag was provided at creation time.
|
||||||
|
*
|
||||||
|
* @return the daemon flag
|
||||||
|
*/
|
||||||
|
public final Boolean getDaemonFlag() {
|
||||||
|
return daemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the priority of the threads created by this factory. Result can
|
||||||
|
* be <b>null</b> if no priority was specified.
|
||||||
|
*
|
||||||
|
* @return the priority for newly created threads
|
||||||
|
*/
|
||||||
|
public final Integer getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@code UncaughtExceptionHandler} for the threads created by
|
||||||
|
* this factory. Result can be <b>null</b> if no handler was provided.
|
||||||
|
*
|
||||||
|
* @return the {@code UncaughtExceptionHandler}
|
||||||
|
*/
|
||||||
|
public final Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
|
||||||
|
return uncaughtExceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of threads this factory has already created. This
|
||||||
|
* class maintains an internal counter that is incremented each time the
|
||||||
|
* {@link #newThread(Runnable)} method is invoked.
|
||||||
|
*
|
||||||
|
* @return the number of threads created by this factory
|
||||||
|
*/
|
||||||
|
public long getThreadCount() {
|
||||||
|
return threadCounter.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new thread. This implementation delegates to the wrapped
|
||||||
|
* factory for creating the thread. Then, on the newly created thread the
|
||||||
|
* corresponding configuration options are set.
|
||||||
|
*
|
||||||
|
* @param runnable the {@code Runnable} to be executed by the new thread
|
||||||
|
* @return the newly created thread
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Thread newThread(final Runnable runnable) {
|
||||||
|
final Thread thread = getWrappedFactory().newThread(runnable);
|
||||||
|
initializeThread(thread);
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the specified thread. This method is called by
|
||||||
|
* {@link #newThread(Runnable)} after a new thread has been obtained from
|
||||||
|
* the wrapped thread factory. It initializes the thread according to the
|
||||||
|
* options set for this factory.
|
||||||
|
*
|
||||||
|
* @param thread the thread to be initialized
|
||||||
|
*/
|
||||||
|
private void initializeThread(final Thread thread) {
|
||||||
|
|
||||||
|
if (getNamingPattern() != null) {
|
||||||
|
final Long count = Long.valueOf(threadCounter.incrementAndGet());
|
||||||
|
thread.setName(String.format(getNamingPattern(), count));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getUncaughtExceptionHandler() != null) {
|
||||||
|
thread.setUncaughtExceptionHandler(getUncaughtExceptionHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getPriority() != null) {
|
||||||
|
thread.setPriority(getPriority().intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getDaemonFlag() != null) {
|
||||||
|
thread.setDaemon(getDaemonFlag().booleanValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A <em>builder</em> class for creating instances of {@code
|
||||||
|
* BasicThreadFactory}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Using this builder class instances of {@code BasicThreadFactory} can be
|
||||||
|
* created and initialized. The class provides methods that correspond to
|
||||||
|
* the configuration options supported by {@code BasicThreadFactory}. Method
|
||||||
|
* chaining is supported. Refer to the documentation of {@code
|
||||||
|
* BasicThreadFactory} for a usage example.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class Builder
|
||||||
|
implements org.apache.commons.lang3.builder.Builder<BasicThreadFactory> {
|
||||||
|
|
||||||
|
/** The wrapped factory. */
|
||||||
|
private ThreadFactory wrappedFactory;
|
||||||
|
|
||||||
|
/** The uncaught exception handler. */
|
||||||
|
private Thread.UncaughtExceptionHandler exceptionHandler;
|
||||||
|
|
||||||
|
/** The naming pattern. */
|
||||||
|
private String namingPattern;
|
||||||
|
|
||||||
|
/** The priority. */
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
/** The daemon flag. */
|
||||||
|
private Boolean daemon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@code ThreadFactory} to be wrapped by the new {@code
|
||||||
|
* BasicThreadFactory}.
|
||||||
|
*
|
||||||
|
* @param factory the wrapped {@code ThreadFactory} (must not be
|
||||||
|
* <b>null</b>)
|
||||||
|
* @return a reference to this {@code Builder}
|
||||||
|
* @throws NullPointerException if the passed in {@code ThreadFactory}
|
||||||
|
* is <b>null</b>
|
||||||
|
*/
|
||||||
|
public Builder wrappedFactory(final ThreadFactory factory) {
|
||||||
|
Validate.notNull(factory, "factory");
|
||||||
|
|
||||||
|
wrappedFactory = factory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the naming pattern to be used by the new {@code
|
||||||
|
* BasicThreadFactory}.
|
||||||
|
*
|
||||||
|
* @param pattern the naming pattern (must not be <b>null</b>)
|
||||||
|
* @return a reference to this {@code Builder}
|
||||||
|
* @throws NullPointerException if the naming pattern is <b>null</b>
|
||||||
|
*/
|
||||||
|
public Builder namingPattern(final String pattern) {
|
||||||
|
Validate.notNull(pattern, "pattern");
|
||||||
|
|
||||||
|
namingPattern = pattern;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the daemon flag for the new {@code BasicThreadFactory}. If this
|
||||||
|
* flag is set to <b>true</b> the new thread factory will create daemon
|
||||||
|
* threads.
|
||||||
|
*
|
||||||
|
* @param daemon the value of the daemon flag
|
||||||
|
* @return a reference to this {@code Builder}
|
||||||
|
*/
|
||||||
|
public Builder daemon(final boolean daemon) {
|
||||||
|
this.daemon = Boolean.valueOf(daemon);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the priority for the threads created by the new {@code
|
||||||
|
* BasicThreadFactory}.
|
||||||
|
*
|
||||||
|
* @param priority the priority
|
||||||
|
* @return a reference to this {@code Builder}
|
||||||
|
*/
|
||||||
|
public Builder priority(final int priority) {
|
||||||
|
this.priority = Integer.valueOf(priority);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the uncaught exception handler for the threads created by the
|
||||||
|
* new {@code BasicThreadFactory}.
|
||||||
|
*
|
||||||
|
* @param handler the {@code UncaughtExceptionHandler} (must not be
|
||||||
|
* <b>null</b>)
|
||||||
|
* @return a reference to this {@code Builder}
|
||||||
|
* @throws NullPointerException if the exception handler is <b>null</b>
|
||||||
|
*/
|
||||||
|
public Builder uncaughtExceptionHandler(
|
||||||
|
final Thread.UncaughtExceptionHandler handler) {
|
||||||
|
Validate.notNull(handler, "handler");
|
||||||
|
|
||||||
|
exceptionHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets this builder. All configuration options are set to default
|
||||||
|
* values. Note: If the {@link #build()} method was called, it is not
|
||||||
|
* necessary to call {@code reset()} explicitly because this is done
|
||||||
|
* automatically.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
wrappedFactory = null;
|
||||||
|
exceptionHandler = null;
|
||||||
|
namingPattern = null;
|
||||||
|
priority = null;
|
||||||
|
daemon = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code BasicThreadFactory} with all configuration
|
||||||
|
* options that have been specified by calling methods on this builder.
|
||||||
|
* After creating the factory {@link #reset()} is called.
|
||||||
|
*
|
||||||
|
* @return the new {@code BasicThreadFactory}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BasicThreadFactory build() {
|
||||||
|
final BasicThreadFactory factory = new BasicThreadFactory(this);
|
||||||
|
reset();
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A specialized {@link BackgroundInitializer} implementation that wraps a
|
||||||
|
* {@code Callable} object.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* An instance of this class is initialized with a {@code Callable} object when
|
||||||
|
* it is constructed. The implementation of the {@link #initialize()} method
|
||||||
|
* defined in the super class delegates to this {@code Callable} so that the
|
||||||
|
* {@code Callable} is executed in the background thread.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The {@code java.util.concurrent.Callable} interface is a standard mechanism
|
||||||
|
* of the JDK to define tasks to be executed by another thread. The {@code
|
||||||
|
* CallableBackgroundInitializer} class allows combining this standard interface
|
||||||
|
* with the background initializer API.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Usage of this class is very similar to the default usage pattern of the
|
||||||
|
* {@link BackgroundInitializer} class: Just create an instance and provide the
|
||||||
|
* {@code Callable} object to be executed, then call the initializer's
|
||||||
|
* {@link #start()} method. This causes the {@code Callable} to be executed in
|
||||||
|
* another thread. When the results of the {@code Callable} are needed the
|
||||||
|
* initializer's {@link #get()} method can be called (which may block until
|
||||||
|
* background execution is complete). The following code fragment shows a
|
||||||
|
* typical usage example:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* // a Callable that performs a complex computation
|
||||||
|
* Callable<Integer> computationCallable = new MyComputationCallable();
|
||||||
|
* // setup the background initializer
|
||||||
|
* CallableBackgroundInitializer<Integer> initializer =
|
||||||
|
* new CallableBackgroundInitializer(computationCallable);
|
||||||
|
* initializer.start();
|
||||||
|
* // Now do some other things. Initialization runs in a parallel thread
|
||||||
|
* ...
|
||||||
|
* // Wait for the end of initialization and access the result
|
||||||
|
* Integer result = initializer.get();
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
* @param <T> the type of the object managed by this initializer class
|
||||||
|
*/
|
||||||
|
public class CallableBackgroundInitializer<T> extends BackgroundInitializer<T> {
|
||||||
|
/** The Callable to be executed. */
|
||||||
|
private final Callable<T> callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code CallableBackgroundInitializer} and sets
|
||||||
|
* the {@code Callable} to be executed in a background thread.
|
||||||
|
*
|
||||||
|
* @param call the {@code Callable} (must not be <b>null</b>)
|
||||||
|
* @throws IllegalArgumentException if the {@code Callable} is <b>null</b>
|
||||||
|
*/
|
||||||
|
public CallableBackgroundInitializer(final Callable<T> call) {
|
||||||
|
checkCallable(call);
|
||||||
|
callable = call;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code CallableBackgroundInitializer} and
|
||||||
|
* initializes it with the {@code Callable} to be executed in a background
|
||||||
|
* thread and the {@code ExecutorService} for managing the background
|
||||||
|
* execution.
|
||||||
|
*
|
||||||
|
* @param call the {@code Callable} (must not be <b>null</b>)
|
||||||
|
* @param exec an external {@code ExecutorService} to be used for task
|
||||||
|
* execution
|
||||||
|
* @throws IllegalArgumentException if the {@code Callable} is <b>null</b>
|
||||||
|
*/
|
||||||
|
public CallableBackgroundInitializer(final Callable<T> call, final ExecutorService exec) {
|
||||||
|
super(exec);
|
||||||
|
checkCallable(call);
|
||||||
|
callable = call;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs initialization in a background thread. This implementation
|
||||||
|
* delegates to the {@code Callable} passed at construction time of this
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @return the result of the initialization
|
||||||
|
* @throws Exception if an error occurs
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected T initialize() throws Exception {
|
||||||
|
return callable.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the passed in {@code Callable} and throws an exception if it is
|
||||||
|
* undefined.
|
||||||
|
*
|
||||||
|
* @param callable the object to check
|
||||||
|
* @throws IllegalArgumentException if the {@code Callable} is <b>null</b>
|
||||||
|
*/
|
||||||
|
private void checkCallable(final Callable<T> callable) {
|
||||||
|
Validate.notNull(callable, "callable");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* An interface describing a <a
|
||||||
|
* href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> component.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* A <em>circuit breaker</em> can be used to protect an application against unreliable
|
||||||
|
* services or unexpected load. It typically monitors a specific resource. As long as this
|
||||||
|
* resource works as expected, it stays in state <em>closed</em>, meaning that the
|
||||||
|
* resource can be used. If problems are encountered when using the resource, the circuit
|
||||||
|
* breaker can switch into state <em>open</em>; then access to this resource is
|
||||||
|
* prohibited. Depending on a concrete implementation, it is possible that the circuit
|
||||||
|
* breaker switches back to state <em>closed</em> when the resource becomes available
|
||||||
|
* again.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This interface defines a generic protocol of a circuit breaker component. It should be
|
||||||
|
* sufficiently generic to be applied to multiple different use cases.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the value monitored by this circuit breaker
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public interface CircuitBreaker<T> {
|
||||||
|
/**
|
||||||
|
* Returns the current open state of this circuit breaker. A return value of
|
||||||
|
* <strong>true</strong> means that the circuit breaker is currently open indicating a
|
||||||
|
* problem in the monitored sub system.
|
||||||
|
*
|
||||||
|
* @return the current open state of this circuit breaker
|
||||||
|
*/
|
||||||
|
boolean isOpen();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current closed state of this circuit breaker. A return value of
|
||||||
|
* <strong>true</strong> means that the circuit breaker is currently closed. This
|
||||||
|
* means that everything is okay with the monitored sub system.
|
||||||
|
*
|
||||||
|
* @return the current closed state of this circuit breaker
|
||||||
|
*/
|
||||||
|
boolean isClosed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the state of this circuit breaker and changes it if necessary. The return
|
||||||
|
* value indicates whether the circuit breaker is now in state {@code CLOSED}; a value
|
||||||
|
* of <strong>true</strong> typically means that the current operation can continue.
|
||||||
|
*
|
||||||
|
* @return <strong>true</strong> if the circuit breaker is now closed;
|
||||||
|
* <strong>false</strong> otherwise
|
||||||
|
*/
|
||||||
|
boolean checkState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes this circuit breaker. Its state is changed to closed. If this circuit
|
||||||
|
* breaker is already closed, this method has no effect.
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens this circuit breaker. Its state is changed to open. Depending on a concrete
|
||||||
|
* implementation, it may close itself again if the monitored sub system becomes
|
||||||
|
* available. If this circuit breaker is already open, this method has no effect.
|
||||||
|
*/
|
||||||
|
void open();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the monitored value and performs a check of the current state of this
|
||||||
|
* circuit breaker. This method works like {@link #checkState()}, but the monitored
|
||||||
|
* value is incremented before the state check is performed.
|
||||||
|
*
|
||||||
|
* @param increment value to increment in the monitored value of the circuit breaker
|
||||||
|
* @return <strong>true</strong> if the circuit breaker is now closed;
|
||||||
|
* <strong>false</strong> otherwise
|
||||||
|
*/
|
||||||
|
boolean incrementAndCheckState(T increment);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue