clone of Guava 28.1
This commit is contained in:
commit
3ab350bb93
569 changed files with 162570 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/data
|
||||||
|
/work
|
||||||
|
/logs
|
||||||
|
/.idea
|
||||||
|
/target
|
||||||
|
.DS_Store
|
||||||
|
*.iml
|
||||||
|
/.settings
|
||||||
|
/.classpath
|
||||||
|
/.project
|
||||||
|
/.gradle
|
||||||
|
/build
|
3
.travis.yml
Normal file
3
.travis.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
language: java
|
||||||
|
jdk:
|
||||||
|
- openjdk11
|
202
LICENSE.txt
Normal file
202
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.
|
29
README.adoc
Normal file
29
README.adoc
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# xbib Guava
|
||||||
|
|
||||||
|
This is xbib Guava, a build of Google Guava with the following differences to the
|
||||||
|
original [Google Guava Library](https://github.com/google/guava):
|
||||||
|
|
||||||
|
- forked master branch on November 21, 2019 ("28.1+")
|
||||||
|
- removed all external annotations, so this library does not have any dependencies
|
||||||
|
- removed duplicate JDK classes (LongAdder, Striped64)
|
||||||
|
- replaced sun.misc.Unsafe dependent classes with safe versions (LongAdders, UnsignedBytes, LittleEndianByteArray, AbstractFuture)
|
||||||
|
- the guava failureaccess dependency is included
|
||||||
|
- removed listenablefuture empty dependency hack
|
||||||
|
- compiled under and for Java 11 and with a module info for JPMS (module org.xbib.guava)
|
||||||
|
- Gradle as build system
|
||||||
|
|
||||||
|
All credits belong to the original authors
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
163
build.gradle
Normal file
163
build.gradle
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
plugins {
|
||||||
|
id "io.codearte.nexus-staging" version "0.21.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'maven'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestJava {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs << "-Xlint:all"
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
testLogging {
|
||||||
|
showStandardStreams = false
|
||||||
|
exceptionFormat = 'full'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||||
|
classifier 'sources'
|
||||||
|
from sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
|
||||||
|
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||||
|
classifier 'javadoc'
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
archives sourcesJar, javadocJar
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
user = 'xbib'
|
||||||
|
projectName = 'guava'
|
||||||
|
projectDescription = 'Guava for Java'
|
||||||
|
scmUrl = 'https://github.com/xbib/guava'
|
||||||
|
scmConnection = 'scm:git:git://github.com/xbib/guava.git'
|
||||||
|
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guava.git'
|
||||||
|
}
|
||||||
|
|
||||||
|
task sonatypeUpload(type: Upload) {
|
||||||
|
group = 'publish'
|
||||||
|
configuration = configurations.archives
|
||||||
|
uploadDescriptor = true
|
||||||
|
repositories {
|
||||||
|
if (project.hasProperty('ossrhUsername')) {
|
||||||
|
mavenDeployer {
|
||||||
|
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||||
|
repository(url: uri(ossrhReleaseUrl)) {
|
||||||
|
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||||
|
}
|
||||||
|
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
|
||||||
|
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||||
|
}
|
||||||
|
pom.project {
|
||||||
|
groupId project.group
|
||||||
|
artifactId project.name
|
||||||
|
version project.version
|
||||||
|
name project.name
|
||||||
|
description projectDescription
|
||||||
|
packaging 'jar'
|
||||||
|
inceptionYear '2019'
|
||||||
|
url scmUrl
|
||||||
|
organization {
|
||||||
|
name 'xbib'
|
||||||
|
url 'http://xbib.org'
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id user
|
||||||
|
name 'Jörg Prante'
|
||||||
|
email 'joergprante@gmail.com'
|
||||||
|
url 'https://github.com/jprante'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
url scmUrl
|
||||||
|
connection scmConnection
|
||||||
|
developerConnection scmDeveloperConnection
|
||||||
|
}
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name 'The Apache License, Version 2.0'
|
||||||
|
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nexusStaging {
|
||||||
|
packageGroup = "org.xbib"
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
spotbugs {
|
||||||
|
effort = "max"
|
||||||
|
reportLevel = "low"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(com.github.spotbugs.SpotBugsTask) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = false
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pmd {
|
||||||
|
toolVersion = '6.11.0'
|
||||||
|
ruleSets = ['category/java/bestpractices.xml']
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Pmd) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = true
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
toolVersion = '8.26'
|
||||||
|
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
|
||||||
|
ignoreFailures = true
|
||||||
|
checkstyleMain {
|
||||||
|
source = sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Checkstyle) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = true
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sonarqube {
|
||||||
|
properties {
|
||||||
|
property "sonar.projectName", "${project.group} ${project.name}"
|
||||||
|
property "sonar.sourceEncoding", "UTF-8"
|
||||||
|
property "sonar.tests", "src/test/java"
|
||||||
|
property "sonar.scm.provider", "git"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
177
config/checkstyle/checkstyle.xml
Normal file
177
config/checkstyle/checkstyle.xml
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
<module name="Checker">
|
||||||
|
<module name="SuppressionFilter">
|
||||||
|
<property name="file" value="${config_loc}/suppressions.xml"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="SuppressWarningsFilter"/>
|
||||||
|
|
||||||
|
<module name="SeverityMatchFilter">
|
||||||
|
<property name="severity" value="info"/>
|
||||||
|
<property name="acceptOnMatch" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="FileTabCharacter">
|
||||||
|
<property name="eachLine" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="max" value="120"/>
|
||||||
|
<property name="ignorePattern" value="^[ \t]*\*.*@.*$"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<property name="tabWidth" value="4"/>
|
||||||
|
|
||||||
|
<module name="SuppressWarningsHolder"/>
|
||||||
|
|
||||||
|
<module name="Indentation">
|
||||||
|
<property name="forceStrictCondition" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ConstantName"/>
|
||||||
|
|
||||||
|
<module name="FinalParameters">
|
||||||
|
<property name="tokens" value="METHOD_DEF, CTOR_DEF, LITERAL_CATCH, FOR_EACH_CLAUSE"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="FinalLocalVariable">
|
||||||
|
<property name="validateEnhancedForLoopVariable" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LocalFinalVariableName"/>
|
||||||
|
|
||||||
|
<module name="LocalVariableName"/>
|
||||||
|
|
||||||
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="MethodName"/>
|
||||||
|
|
||||||
|
<module name="PackageName"/>
|
||||||
|
|
||||||
|
<module name="ParameterName"/>
|
||||||
|
|
||||||
|
<module name="StaticVariableName"/>
|
||||||
|
|
||||||
|
<module name="TypeName"/>
|
||||||
|
|
||||||
|
<module name="RedundantImport"/>
|
||||||
|
|
||||||
|
<module name="UnusedImports"/>
|
||||||
|
|
||||||
|
<module name="MethodLength">
|
||||||
|
<property name="tokens" value="METHOD_DEF"/>
|
||||||
|
<property name="max" value="100"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="EmptyForInitializerPad"/>
|
||||||
|
|
||||||
|
<module name="MethodParamPad"/>
|
||||||
|
|
||||||
|
<module name="NoWhitespaceBefore"/>
|
||||||
|
|
||||||
|
<module name="WhitespaceAfter">
|
||||||
|
<property name="tokens" value="COMMA, SEMI, LITERAL_IF, LITERAL_ELSE, LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NoWhitespaceAfter">
|
||||||
|
<property name="tokens" value="INC, DEC, UNARY_MINUS, UNARY_PLUS, BNOT, LNOT, DOT, TYPECAST, ARRAY_DECLARATOR, INDEX_OP, METHOD_REF"/>
|
||||||
|
<property name="allowLineBreaks" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property name="allowEmptyLambdas" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="SingleSpaceSeparator"/>
|
||||||
|
|
||||||
|
<module name="OperatorWrap">
|
||||||
|
<property name="option" value="eol"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NeedBraces"/>
|
||||||
|
|
||||||
|
<module name="ParenPad"/>
|
||||||
|
|
||||||
|
<module name="TypecastParenPad"/>
|
||||||
|
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
|
||||||
|
<module name="RedundantModifier"/>
|
||||||
|
|
||||||
|
<module name="NestedTryDepth">
|
||||||
|
<property name="max" value="2"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="CovariantEquals"/>
|
||||||
|
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="option" value="alone"/>
|
||||||
|
<property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="EmptyStatement"/>
|
||||||
|
|
||||||
|
<module name="EqualsHashCode"/>
|
||||||
|
|
||||||
|
<module name="DefaultComesLast"/>
|
||||||
|
|
||||||
|
<module name="SimplifyBooleanExpression"/>
|
||||||
|
|
||||||
|
<module name="SimplifyBooleanReturn"/>
|
||||||
|
|
||||||
|
<module name="StringLiteralEquality"/>
|
||||||
|
|
||||||
|
<module name="PackageDeclaration"/>
|
||||||
|
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
|
||||||
|
<module name="FinalClass"/>
|
||||||
|
|
||||||
|
<module name="MutableException"/>
|
||||||
|
|
||||||
|
<module name="EmptyLineSeparator">
|
||||||
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
|
<property name="tokens" value="IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF, STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TodoComment">
|
||||||
|
<property name="severity" value="info"/>
|
||||||
|
<property name="format" value="TODO"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="UpperEll"/>
|
||||||
|
|
||||||
|
<module name="IllegalType">
|
||||||
|
<property name="legalAbstractClassNames"
|
||||||
|
value="AbstractBeanDefinition, AbstractEntry"/>
|
||||||
|
<property name="illegalClassNames"
|
||||||
|
value="java.util.GregorianCalendar, java.util.Vector"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="DescendantToken">
|
||||||
|
<property name="tokens" value="LITERAL_ASSERT"/>
|
||||||
|
<property name="limitedTokens"
|
||||||
|
value="ASSIGN,DEC,INC,POST_DEC,POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN,BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,METHOD_CALL"/>
|
||||||
|
<property name="maximumNumber" value="2"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="Regexp">
|
||||||
|
<property name="format" value="[ \t]+$"/>
|
||||||
|
<property name="illegalPattern" value="true"/>
|
||||||
|
<property name="message" value="Trailing whitespace"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="allowUndeclaredRTE" value="true"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
</module>
|
7
config/checkstyle/suppressions.xml
Normal file
7
config/checkstyle/suppressions.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE suppressions PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Suppressions 1.0//EN"
|
||||||
|
"https://checkstyle.org/dtds/suppressions_1_0.dtd">
|
||||||
|
<suppressions>
|
||||||
|
<suppress files=".*generated-src.*" checks="."/>
|
||||||
|
</suppressions>
|
3
gradle.properties
Normal file
3
gradle.properties
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
group = org.xbib
|
||||||
|
name = guava
|
||||||
|
version = 28.1
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#Sat Nov 23 21:29:06 CET 2019
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
188
gradlew
vendored
Executable file
188
gradlew
vendored
Executable file
|
@ -0,0 +1,188 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
100
gradlew.bat
vendored
Normal file
100
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = name
|
47
src/main/java/com/google/common/annotations/Beta.java
Normal file
47
src/main/java/com/google/common/annotations/Beta.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signifies that a public API (public class, method or field) is subject to incompatible changes,
|
||||||
|
* or even removal, in a future release. An API bearing this annotation is exempt from any
|
||||||
|
* compatibility guarantees made by its containing library. Note that the presence of this
|
||||||
|
* annotation implies nothing about the quality or performance of the API in question, only the fact
|
||||||
|
* that it is not "API-frozen."
|
||||||
|
*
|
||||||
|
* <p>It is generally safe for <i>applications</i> to depend on beta APIs, at the cost of some extra
|
||||||
|
* work during upgrades. However it is generally inadvisable for <i>libraries</i> (which get
|
||||||
|
* included on users' CLASSPATHs, outside the library developers' control) to do so.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target({
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.TYPE
|
||||||
|
})
|
||||||
|
@Documented
|
||||||
|
@GwtCompatible
|
||||||
|
public @interface Beta {}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presence of this annotation on a type indicates that the type may be used with the <a
|
||||||
|
* href="http://code.google.com/webtoolkit/">Google Web Toolkit</a> (GWT). When applied to a method,
|
||||||
|
* the return type of the method is GWT compatible. It's useful to indicate that an instance created
|
||||||
|
* by factory methods has a GWT serializable type. In the following example,
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@literal @}GwtCompatible
|
||||||
|
* class Lists {
|
||||||
|
* ...
|
||||||
|
* {@literal @}GwtCompatible(serializable = true)
|
||||||
|
* {@literal static <E> List<E>} newArrayList(E... elements) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>The return value of {@code Lists.newArrayList(E[])} has GWT serializable type. It is also
|
||||||
|
* useful in specifying contracts of interface methods. In the following example,
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@literal @}GwtCompatible
|
||||||
|
* interface ListFactory {
|
||||||
|
* ...
|
||||||
|
* {@literal @}GwtCompatible(serializable = true)
|
||||||
|
* {@literal <E> List<E>} newArrayList(E... elements);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>The {@code newArrayList(E[])} method of all implementations of {@code ListFactory} is expected
|
||||||
|
* to return a value with a GWT serializable type.
|
||||||
|
*
|
||||||
|
* <p>Note that a {@code GwtCompatible} type may have some {@link GwtIncompatible} methods.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @author Hayward Chan
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
|
@Documented
|
||||||
|
@GwtCompatible
|
||||||
|
public @interface GwtCompatible {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When {@code true}, the annotated type or the type of the method return value is GWT
|
||||||
|
* serializable.
|
||||||
|
*
|
||||||
|
* @see <a href=
|
||||||
|
* "http://code.google.com/webtoolkit/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes">
|
||||||
|
* Documentation about GWT serialization</a>
|
||||||
|
*/
|
||||||
|
boolean serializable() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When {@code true}, the annotated type is emulated in GWT. The emulated source (also known as
|
||||||
|
* super-source) is different from the implementation used by the JVM.
|
||||||
|
*
|
||||||
|
* @see <a href=
|
||||||
|
* "http://code.google.com/webtoolkit/doc/latest/DevGuideOrganizingProjects.html#DevGuideModules">
|
||||||
|
* Documentation about GWT emulated source</a>
|
||||||
|
*/
|
||||||
|
boolean emulated() default false;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presence of this annotation on an API indicates that the method may <em>not</em> be used with
|
||||||
|
* the <a href="http://www.gwtproject.org/">Google Web Toolkit</a> (GWT).
|
||||||
|
*
|
||||||
|
* <p>This annotation behaves identically to <a href=
|
||||||
|
* "http://www.gwtproject.org/javadoc/latest/com/google/gwt/core/shared/GwtIncompatible.html">the
|
||||||
|
* {@code @GwtIncompatible} annotation in GWT itself</a>.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
|
||||||
|
@Documented
|
||||||
|
@GwtCompatible
|
||||||
|
public @interface GwtIncompatible {
|
||||||
|
/**
|
||||||
|
* Describes why the annotated element is incompatible with GWT. Since this is generally due to a
|
||||||
|
* dependence on a type/method which GWT doesn't support, it is sufficient to simply reference the
|
||||||
|
* unsupported type/method. E.g. "Class.isInstance".
|
||||||
|
*
|
||||||
|
* <p>As of Guava 20.0, this value is optional. We encourage authors who wish to describe why an
|
||||||
|
* API is {@code @GwtIncompatible} to instead leave an implementation comment.
|
||||||
|
*/
|
||||||
|
String value() default "";
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2006 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.annotations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates a program element that exists, or is more widely visible than otherwise necessary, only
|
||||||
|
* for use in test code.
|
||||||
|
*
|
||||||
|
* @author Johannes Henkel
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public @interface VisibleForTesting {
|
||||||
|
}
|
98
src/main/java/com/google/common/base/Absent.java
Normal file
98
src/main/java/com/google/common/base/Absent.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Implementation of an {@link Optional} not containing a reference. */
|
||||||
|
@GwtCompatible
|
||||||
|
final class Absent<T> extends Optional<T> {
|
||||||
|
static final Absent<Object> INSTANCE = new Absent<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // implementation is "fully variant"
|
||||||
|
static <T> Optional<T> withType() {
|
||||||
|
return (Optional<T>) INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Absent() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPresent() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
throw new IllegalStateException("Optional.get() cannot be called on an absent value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T or(T defaultValue) {
|
||||||
|
return checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // safe covariant cast
|
||||||
|
@Override
|
||||||
|
public Optional<T> or(Optional<? extends T> secondChoice) {
|
||||||
|
return (Optional<T>) checkNotNull(secondChoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T or(Supplier<? extends T> supplier) {
|
||||||
|
return checkNotNull(
|
||||||
|
supplier.get(), "use Optional.orNull() instead of a Supplier that returns null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T orNull() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<T> asSet() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> Optional<V> transform(Function<? super T, V> function) {
|
||||||
|
checkNotNull(function);
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
return object == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0x79a31aac;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Optional.absent()";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResolve() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
87
src/main/java/com/google/common/base/AbstractIterator.java
Normal file
87
src/main/java/com/google/common/base/AbstractIterator.java
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note this class is a copy of {@link com.google.common.collect.AbstractIterator} (for dependency
|
||||||
|
* reasons).
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractIterator<T> implements Iterator<T> {
|
||||||
|
private State state = State.NOT_READY;
|
||||||
|
|
||||||
|
protected AbstractIterator() {}
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
READY,
|
||||||
|
NOT_READY,
|
||||||
|
DONE,
|
||||||
|
FAILED,
|
||||||
|
}
|
||||||
|
|
||||||
|
private T next;
|
||||||
|
|
||||||
|
protected abstract T computeNext();
|
||||||
|
|
||||||
|
protected final T endOfData() {
|
||||||
|
state = State.DONE;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean hasNext() {
|
||||||
|
checkState(state != State.FAILED);
|
||||||
|
switch (state) {
|
||||||
|
case DONE:
|
||||||
|
return false;
|
||||||
|
case READY:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return tryToComputeNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryToComputeNext() {
|
||||||
|
state = State.FAILED; // temporary pessimism
|
||||||
|
next = computeNext();
|
||||||
|
if (state != State.DONE) {
|
||||||
|
state = State.READY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final T next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
state = State.NOT_READY;
|
||||||
|
T result = next;
|
||||||
|
next = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
635
src/main/java/com/google/common/base/Ascii.java
Normal file
635
src/main/java/com/google/common/base/Ascii.java
Normal file
|
@ -0,0 +1,635 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static methods pertaining to ASCII characters (those in the range of values {@code 0x00} through
|
||||||
|
* {@code 0x7F}), and to strings containing such characters.
|
||||||
|
*
|
||||||
|
* <p>ASCII utilities also exist in other classes of this package:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <!-- TODO(kevinb): how can we make this not produce a warning when building gwt javadoc? -->
|
||||||
|
* <li>{@link Charsets#US_ASCII} specifies the {@code Charset} of ASCII characters.
|
||||||
|
* <li>{@link CharMatcher#ascii} matches ASCII characters and provides text processing methods
|
||||||
|
* which operate only on the ASCII characters of a string.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Catherine Berry
|
||||||
|
* @author Gregory Kick
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class Ascii {
|
||||||
|
|
||||||
|
private Ascii() {}
|
||||||
|
|
||||||
|
/* The ASCII control characters, per RFC 20. */
|
||||||
|
/**
|
||||||
|
* Null ('\0'): The all-zeros character which may serve to accomplish time fill and media fill.
|
||||||
|
* Normally used as a C string terminator.
|
||||||
|
*
|
||||||
|
* <p>Although RFC 20 names this as "Null", note that it is distinct from the C/C++ "NULL"
|
||||||
|
* pointer.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte NUL = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start of Heading: A communication control character used at the beginning of a sequence of
|
||||||
|
* characters which constitute a machine-sensible address or routing information. Such a sequence
|
||||||
|
* is referred to as the "heading." An STX character has the effect of terminating a heading.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SOH = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start of Text: A communication control character which precedes a sequence of characters that
|
||||||
|
* is to be treated as an entity and entirely transmitted through to the ultimate destination.
|
||||||
|
* Such a sequence is referred to as "text." STX may be used to terminate a sequence of characters
|
||||||
|
* started by SOH.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte STX = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End of Text: A communication control character used to terminate a sequence of characters
|
||||||
|
* started with STX and transmitted as an entity.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte ETX = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End of Transmission: A communication control character used to indicate the conclusion of a
|
||||||
|
* transmission, which may have contained one or more texts and any associated headings.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte EOT = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enquiry: A communication control character used in data communication systems as a request for
|
||||||
|
* a response from a remote station. It may be used as a "Who Are You" (WRU) to obtain
|
||||||
|
* identification, or may be used to obtain station status, or both.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte ENQ = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledge: A communication control character transmitted by a receiver as an affirmative
|
||||||
|
* response to a sender.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte ACK = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bell ('\a'): A character for use when there is a need to call for human attention. It may
|
||||||
|
* control alarm or attention devices.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte BEL = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backspace ('\b'): A format effector which controls the movement of the printing position one
|
||||||
|
* printing space backward on the same printing line. (Applicable also to display devices.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte BS = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal Tabulation ('\t'): A format effector which controls the movement of the printing
|
||||||
|
* position to the next in a series of predetermined positions along the printing line.
|
||||||
|
* (Applicable also to display devices and the skip function on punched cards.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte HT = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Line Feed ('\n'): A format effector which controls the movement of the printing position to the
|
||||||
|
* next printing line. (Applicable also to display devices.) Where appropriate, this character may
|
||||||
|
* have the meaning "New Line" (NL), a format effector which controls the movement of the printing
|
||||||
|
* point to the first printing position on the next printing line. Use of this convention requires
|
||||||
|
* agreement between sender and recipient of data.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte LF = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternate name for {@link #LF}. ({@code LF} is preferred.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte NL = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical Tabulation ('\v'): A format effector which controls the movement of the printing
|
||||||
|
* position to the next in a series of predetermined printing lines. (Applicable also to display
|
||||||
|
* devices.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte VT = 11;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form Feed ('\f'): A format effector which controls the movement of the printing position to the
|
||||||
|
* first pre-determined printing line on the next form or page. (Applicable also to display
|
||||||
|
* devices.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte FF = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carriage Return ('\r'): A format effector which controls the movement of the printing position
|
||||||
|
* to the first printing position on the same printing line. (Applicable also to display devices.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte CR = 13;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift Out: A control character indicating that the code combinations which follow shall be
|
||||||
|
* interpreted as outside of the character set of the standard code table until a Shift In
|
||||||
|
* character is reached.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SO = 14;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift In: A control character indicating that the code combinations which follow shall be
|
||||||
|
* interpreted according to the standard code table.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SI = 15;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Link Escape: A communication control character which will change the meaning of a limited
|
||||||
|
* number of contiguously following characters. It is used exclusively to provide supplementary
|
||||||
|
* controls in data communication networks.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte DLE = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device Control 1. Characters for the control of ancillary devices associated with data
|
||||||
|
* processing or telecommunication systems, more especially switching devices "on" or "off." (If a
|
||||||
|
* single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the
|
||||||
|
* preferred assignment.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte DC1 = 17; // aka XON
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmission On: Although originally defined as DC1, this ASCII control character is now better
|
||||||
|
* known as the XON code used for software flow control in serial communications. The main use is
|
||||||
|
* restarting the transmission after the communication has been stopped by the XOFF control code.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte XON = 17; // aka DC1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device Control 2. Characters for the control of ancillary devices associated with data
|
||||||
|
* processing or telecommunication systems, more especially switching devices "on" or "off." (If a
|
||||||
|
* single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the
|
||||||
|
* preferred assignment.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte DC2 = 18;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device Control 3. Characters for the control of ancillary devices associated with data
|
||||||
|
* processing or telecommunication systems, more especially switching devices "on" or "off." (If a
|
||||||
|
* single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the
|
||||||
|
* preferred assignment.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte DC3 = 19; // aka XOFF
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmission off. See {@link #XON} for explanation.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte XOFF = 19; // aka DC3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device Control 4. Characters for the control of ancillary devices associated with data
|
||||||
|
* processing or telecommunication systems, more especially switching devices "on" or "off." (If a
|
||||||
|
* single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the
|
||||||
|
* preferred assignment.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte DC4 = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Negative Acknowledge: A communication control character transmitted by a receiver as a negative
|
||||||
|
* response to the sender.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte NAK = 21;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronous Idle: A communication control character used by a synchronous transmission system
|
||||||
|
* in the absence of any other character to provide a signal from which synchronism may be
|
||||||
|
* achieved or retained.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SYN = 22;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End of Transmission Block: A communication control character used to indicate the end of a
|
||||||
|
* block of data for communication purposes. ETB is used for blocking data where the block
|
||||||
|
* structure is not necessarily related to the processing format.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte ETB = 23;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel: A control character used to indicate that the data with which it is sent is in error or
|
||||||
|
* is to be disregarded.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte CAN = 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End of Medium: A control character associated with the sent data which may be used to identify
|
||||||
|
* the physical end of the medium, or the end of the used, or wanted, portion of information
|
||||||
|
* recorded on a medium. (The position of this character does not necessarily correspond to the
|
||||||
|
* physical end of the medium.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte EM = 25;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substitute: A character that may be substituted for a character which is determined to be
|
||||||
|
* invalid or in error.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SUB = 26;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape: A control character intended to provide code extension (supplementary characters) in
|
||||||
|
* general information interchange. The Escape character itself is a prefix affecting the
|
||||||
|
* interpretation of a limited number of contiguously following characters.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte ESC = 27;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File Separator: These four information separators may be used within data in optional fashion,
|
||||||
|
* except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then
|
||||||
|
* RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are
|
||||||
|
* not specified.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte FS = 28;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group Separator: These four information separators may be used within data in optional fashion,
|
||||||
|
* except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then
|
||||||
|
* RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are
|
||||||
|
* not specified.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte GS = 29;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record Separator: These four information separators may be used within data in optional
|
||||||
|
* fashion, except that their hierarchical relationship shall be: FS is the most inclusive, then
|
||||||
|
* GS, then RS, and US is least inclusive. (The content and length of a File, Group, Record, or
|
||||||
|
* Unit are not specified.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte RS = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit Separator: These four information separators may be used within data in optional fashion,
|
||||||
|
* except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then
|
||||||
|
* RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are
|
||||||
|
* not specified.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte US = 31;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Space: A normally non-printing graphic character used to separate words. It is also a format
|
||||||
|
* effector which controls the movement of the printing position, one printing position forward.
|
||||||
|
* (Applicable also to display devices.)
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SP = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternate name for {@link #SP}.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte SPACE = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete: This character is used primarily to "erase" or "obliterate" erroneous or unwanted
|
||||||
|
* characters in perforated tape.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static final byte DEL = 127;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum value of an ASCII character.
|
||||||
|
*
|
||||||
|
* @since 9.0 (was type {@code int} before 12.0)
|
||||||
|
*/
|
||||||
|
public static final char MIN = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum value of an ASCII character.
|
||||||
|
*
|
||||||
|
* @since 9.0 (was type {@code int} before 12.0)
|
||||||
|
*/
|
||||||
|
public static final char MAX = 127;
|
||||||
|
|
||||||
|
/** A bit mask which selects the bit encoding ASCII character case. */
|
||||||
|
private static final char CASE_MASK = 0x20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the input string in which all {@linkplain #isUpperCase(char) uppercase ASCII
|
||||||
|
* characters} have been converted to lowercase. All other characters are copied without
|
||||||
|
* modification.
|
||||||
|
*/
|
||||||
|
public static String toLowerCase(String string) {
|
||||||
|
int length = string.length();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (isUpperCase(string.charAt(i))) {
|
||||||
|
char[] chars = string.toCharArray();
|
||||||
|
for (; i < length; i++) {
|
||||||
|
char c = chars[i];
|
||||||
|
if (isUpperCase(c)) {
|
||||||
|
chars[i] = (char) (c ^ CASE_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.valueOf(chars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the input character sequence in which all {@linkplain #isUpperCase(char)
|
||||||
|
* uppercase ASCII characters} have been converted to lowercase. All other characters are copied
|
||||||
|
* without modification.
|
||||||
|
*
|
||||||
|
* @since 14.0
|
||||||
|
*/
|
||||||
|
public static String toLowerCase(CharSequence chars) {
|
||||||
|
if (chars instanceof String) {
|
||||||
|
return toLowerCase((String) chars);
|
||||||
|
}
|
||||||
|
char[] newChars = new char[chars.length()];
|
||||||
|
for (int i = 0; i < newChars.length; i++) {
|
||||||
|
newChars[i] = toLowerCase(chars.charAt(i));
|
||||||
|
}
|
||||||
|
return String.valueOf(newChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the argument is an {@linkplain #isUpperCase(char) uppercase ASCII character} returns the
|
||||||
|
* lowercase equivalent. Otherwise returns the argument.
|
||||||
|
*/
|
||||||
|
public static char toLowerCase(char c) {
|
||||||
|
return isUpperCase(c) ? (char) (c ^ CASE_MASK) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the input string in which all {@linkplain #isLowerCase(char) lowercase ASCII
|
||||||
|
* characters} have been converted to uppercase. All other characters are copied without
|
||||||
|
* modification.
|
||||||
|
*/
|
||||||
|
public static String toUpperCase(String string) {
|
||||||
|
int length = string.length();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (isLowerCase(string.charAt(i))) {
|
||||||
|
char[] chars = string.toCharArray();
|
||||||
|
for (; i < length; i++) {
|
||||||
|
char c = chars[i];
|
||||||
|
if (isLowerCase(c)) {
|
||||||
|
chars[i] = (char) (c ^ CASE_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.valueOf(chars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the input character sequence in which all {@linkplain #isLowerCase(char)
|
||||||
|
* lowercase ASCII characters} have been converted to uppercase. All other characters are copied
|
||||||
|
* without modification.
|
||||||
|
*
|
||||||
|
* @since 14.0
|
||||||
|
*/
|
||||||
|
public static String toUpperCase(CharSequence chars) {
|
||||||
|
if (chars instanceof String) {
|
||||||
|
return toUpperCase((String) chars);
|
||||||
|
}
|
||||||
|
char[] newChars = new char[chars.length()];
|
||||||
|
for (int i = 0; i < newChars.length; i++) {
|
||||||
|
newChars[i] = toUpperCase(chars.charAt(i));
|
||||||
|
}
|
||||||
|
return String.valueOf(newChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the argument is a {@linkplain #isLowerCase(char) lowercase ASCII character} returns the
|
||||||
|
* uppercase equivalent. Otherwise returns the argument.
|
||||||
|
*/
|
||||||
|
public static char toUpperCase(char c) {
|
||||||
|
return isLowerCase(c) ? (char) (c ^ CASE_MASK) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether {@code c} is one of the twenty-six lowercase ASCII alphabetic characters
|
||||||
|
* between {@code 'a'} and {@code 'z'} inclusive. All others (including non-ASCII characters)
|
||||||
|
* return {@code false}.
|
||||||
|
*/
|
||||||
|
public static boolean isLowerCase(char c) {
|
||||||
|
// Note: This was benchmarked against the alternate expression "(char)(c - 'a') < 26" (Nov '13)
|
||||||
|
// and found to perform at least as well, or better.
|
||||||
|
return (c >= 'a') && (c <= 'z');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether {@code c} is one of the twenty-six uppercase ASCII alphabetic characters
|
||||||
|
* between {@code 'A'} and {@code 'Z'} inclusive. All others (including non-ASCII characters)
|
||||||
|
* return {@code false}.
|
||||||
|
*/
|
||||||
|
public static boolean isUpperCase(char c) {
|
||||||
|
return (c >= 'A') && (c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncates the given character sequence to the given maximum length. If the length of the
|
||||||
|
* sequence is greater than {@code maxLength}, the returned string will be exactly {@code
|
||||||
|
* maxLength} chars in length and will end with the given {@code truncationIndicator}. Otherwise,
|
||||||
|
* the sequence will be returned as a string with no changes to the content.
|
||||||
|
*
|
||||||
|
* <p>Examples:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Ascii.truncate("foobar", 7, "..."); // returns "foobar"
|
||||||
|
* Ascii.truncate("foobar", 5, "..."); // returns "fo..."
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> This method <i>may</i> work with certain non-ASCII text but is not safe for use
|
||||||
|
* with arbitrary Unicode text. It is mostly intended for use with text that is known to be safe
|
||||||
|
* for use with it (such as all-ASCII text) and for simple debugging text. When using this method,
|
||||||
|
* consider the following:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>it may split surrogate pairs
|
||||||
|
* <li>it may split characters and combining characters
|
||||||
|
* <li>it does not consider word boundaries
|
||||||
|
* <li>if truncating for display to users, there are other considerations that must be taken
|
||||||
|
* into account
|
||||||
|
* <li>the appropriate truncation indicator may be locale-dependent
|
||||||
|
* <li>it is safe to use non-ASCII characters in the truncation indicator
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code maxLength} is less than the length of {@code
|
||||||
|
* truncationIndicator}
|
||||||
|
* @since 16.0
|
||||||
|
*/
|
||||||
|
public static String truncate(CharSequence seq, int maxLength, String truncationIndicator) {
|
||||||
|
checkNotNull(seq);
|
||||||
|
|
||||||
|
// length to truncate the sequence to, not including the truncation indicator
|
||||||
|
int truncationLength = maxLength - truncationIndicator.length();
|
||||||
|
|
||||||
|
// in this worst case, this allows a maxLength equal to the length of the truncationIndicator,
|
||||||
|
// meaning that a string will be truncated to just the truncation indicator itself
|
||||||
|
checkArgument(
|
||||||
|
truncationLength >= 0,
|
||||||
|
"maxLength (%s) must be >= length of the truncation indicator (%s)",
|
||||||
|
maxLength,
|
||||||
|
truncationIndicator.length());
|
||||||
|
|
||||||
|
if (seq.length() <= maxLength) {
|
||||||
|
String string = seq.toString();
|
||||||
|
if (string.length() <= maxLength) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
// if the length of the toString() result was > maxLength for some reason, truncate that
|
||||||
|
seq = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StringBuilder(maxLength)
|
||||||
|
.append(seq, 0, truncationLength)
|
||||||
|
.append(truncationIndicator)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the contents of the given character sequences {@code s1} and {@code s2} are
|
||||||
|
* equal, ignoring the case of any ASCII alphabetic characters between {@code 'a'} and {@code 'z'}
|
||||||
|
* or {@code 'A'} and {@code 'Z'} inclusive.
|
||||||
|
*
|
||||||
|
* <p>This method is significantly faster than {@link String#equalsIgnoreCase} and should be used
|
||||||
|
* in preference if at least one of the parameters is known to contain only ASCII characters.
|
||||||
|
*
|
||||||
|
* <p>Note however that this method does not always behave identically to expressions such as:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code string.toUpperCase().equals("UPPER CASE ASCII")}
|
||||||
|
* <li>{@code string.toLowerCase().equals("lower case ascii")}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>due to case-folding of some non-ASCII characters (which does not occur in {@link
|
||||||
|
* String#equalsIgnoreCase}). However in almost all cases that ASCII strings are used, the author
|
||||||
|
* probably wanted the behavior provided by this method rather than the subtle and sometimes
|
||||||
|
* surprising behavior of {@code toUpperCase()} and {@code toLowerCase()}.
|
||||||
|
*
|
||||||
|
* @since 16.0
|
||||||
|
*/
|
||||||
|
public static boolean equalsIgnoreCase(CharSequence s1, CharSequence s2) {
|
||||||
|
// Calling length() is the null pointer check (so do it before we can exit early).
|
||||||
|
int length = s1.length();
|
||||||
|
if (s1 == s2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (length != s2.length()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c1 = s1.charAt(i);
|
||||||
|
char c2 = s2.charAt(i);
|
||||||
|
if (c1 == c2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int alphaIndex = getAlphaIndex(c1);
|
||||||
|
// This was also benchmarked using '&' to avoid branching (but always evaluate the rhs),
|
||||||
|
// however this showed no obvious improvement.
|
||||||
|
if (alphaIndex < 26 && alphaIndex == getAlphaIndex(c2)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the non-negative index value of the alpha character {@code c}, regardless of case. Ie,
|
||||||
|
* 'a'/'A' returns 0 and 'z'/'Z' returns 25. Non-alpha characters return a value of 26 or greater.
|
||||||
|
*/
|
||||||
|
private static int getAlphaIndex(char c) {
|
||||||
|
// Fold upper-case ASCII to lower-case and make zero-indexed and unsigned (by casting to char).
|
||||||
|
return (char) ((c | CASE_MASK) - 'a');
|
||||||
|
}
|
||||||
|
}
|
213
src/main/java/com/google/common/base/CaseFormat.java
Normal file
213
src/main/java/com/google/common/base/CaseFormat.java
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2006 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for converting between various ASCII case formats. Behavior is undefined for
|
||||||
|
* non-ASCII input.
|
||||||
|
*
|
||||||
|
* @author Mike Bostock
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public enum CaseFormat {
|
||||||
|
/** Hyphenated variable naming convention, e.g., "lower-hyphen". */
|
||||||
|
LOWER_HYPHEN(CharMatcher.is('-'), "-") {
|
||||||
|
@Override
|
||||||
|
String normalizeWord(String word) {
|
||||||
|
return Ascii.toLowerCase(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String convert(CaseFormat format, String s) {
|
||||||
|
if (format == LOWER_UNDERSCORE) {
|
||||||
|
return s.replace('-', '_');
|
||||||
|
}
|
||||||
|
if (format == UPPER_UNDERSCORE) {
|
||||||
|
return Ascii.toUpperCase(s.replace('-', '_'));
|
||||||
|
}
|
||||||
|
return super.convert(format, s);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** C++ variable naming convention, e.g., "lower_underscore". */
|
||||||
|
LOWER_UNDERSCORE(CharMatcher.is('_'), "_") {
|
||||||
|
@Override
|
||||||
|
String normalizeWord(String word) {
|
||||||
|
return Ascii.toLowerCase(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String convert(CaseFormat format, String s) {
|
||||||
|
if (format == LOWER_HYPHEN) {
|
||||||
|
return s.replace('_', '-');
|
||||||
|
}
|
||||||
|
if (format == UPPER_UNDERSCORE) {
|
||||||
|
return Ascii.toUpperCase(s);
|
||||||
|
}
|
||||||
|
return super.convert(format, s);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Java variable naming convention, e.g., "lowerCamel". */
|
||||||
|
LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") {
|
||||||
|
@Override
|
||||||
|
String normalizeWord(String word) {
|
||||||
|
return firstCharOnlyToUpper(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String normalizeFirstWord(String word) {
|
||||||
|
return Ascii.toLowerCase(word);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Java and C++ class naming convention, e.g., "UpperCamel". */
|
||||||
|
UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") {
|
||||||
|
@Override
|
||||||
|
String normalizeWord(String word) {
|
||||||
|
return firstCharOnlyToUpper(word);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". */
|
||||||
|
UPPER_UNDERSCORE(CharMatcher.is('_'), "_") {
|
||||||
|
@Override
|
||||||
|
String normalizeWord(String word) {
|
||||||
|
return Ascii.toUpperCase(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String convert(CaseFormat format, String s) {
|
||||||
|
if (format == LOWER_HYPHEN) {
|
||||||
|
return Ascii.toLowerCase(s.replace('_', '-'));
|
||||||
|
}
|
||||||
|
if (format == LOWER_UNDERSCORE) {
|
||||||
|
return Ascii.toLowerCase(s);
|
||||||
|
}
|
||||||
|
return super.convert(format, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final CharMatcher wordBoundary;
|
||||||
|
private final String wordSeparator;
|
||||||
|
|
||||||
|
CaseFormat(CharMatcher wordBoundary, String wordSeparator) {
|
||||||
|
this.wordBoundary = wordBoundary;
|
||||||
|
this.wordSeparator = wordSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the specified {@code String str} from this format to the specified {@code format}. A
|
||||||
|
* "best effort" approach is taken; if {@code str} does not conform to the assumed format, then
|
||||||
|
* the behavior of this method is undefined but we make a reasonable effort at converting anyway.
|
||||||
|
*/
|
||||||
|
public final String to(CaseFormat format, String str) {
|
||||||
|
checkNotNull(format);
|
||||||
|
checkNotNull(str);
|
||||||
|
return (format == this) ? str : convert(format, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enum values can override for performance reasons. */
|
||||||
|
String convert(CaseFormat format, String s) {
|
||||||
|
// deal with camel conversion
|
||||||
|
StringBuilder out = null;
|
||||||
|
int i = 0;
|
||||||
|
int j = -1;
|
||||||
|
while ((j = wordBoundary.indexIn(s, ++j)) != -1) {
|
||||||
|
if (i == 0) {
|
||||||
|
// include some extra space for separators
|
||||||
|
out = new StringBuilder(s.length() + 4 * wordSeparator.length());
|
||||||
|
out.append(format.normalizeFirstWord(s.substring(i, j)));
|
||||||
|
} else {
|
||||||
|
out.append(format.normalizeWord(s.substring(i, j)));
|
||||||
|
}
|
||||||
|
out.append(format.wordSeparator);
|
||||||
|
i = j + wordSeparator.length();
|
||||||
|
}
|
||||||
|
return (i == 0)
|
||||||
|
? format.normalizeFirstWord(s)
|
||||||
|
: out.append(format.normalizeWord(s.substring(i))).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Converter} that converts strings from this format to {@code targetFormat}.
|
||||||
|
*
|
||||||
|
* @since 16.0
|
||||||
|
*/
|
||||||
|
public Converter<String, String> converterTo(CaseFormat targetFormat) {
|
||||||
|
return new StringConverter(this, targetFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class StringConverter extends Converter<String, String>
|
||||||
|
implements Serializable {
|
||||||
|
|
||||||
|
private final CaseFormat sourceFormat;
|
||||||
|
private final CaseFormat targetFormat;
|
||||||
|
|
||||||
|
StringConverter(CaseFormat sourceFormat, CaseFormat targetFormat) {
|
||||||
|
this.sourceFormat = checkNotNull(sourceFormat);
|
||||||
|
this.targetFormat = checkNotNull(targetFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doForward(String s) {
|
||||||
|
return sourceFormat.to(targetFormat, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doBackward(String s) {
|
||||||
|
return targetFormat.to(sourceFormat, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof StringConverter) {
|
||||||
|
StringConverter that = (StringConverter) object;
|
||||||
|
return sourceFormat.equals(that.sourceFormat) && targetFormat.equals(that.targetFormat);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return sourceFormat.hashCode() ^ targetFormat.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return sourceFormat + ".converterTo(" + targetFormat + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract String normalizeWord(String word);
|
||||||
|
|
||||||
|
String normalizeFirstWord(String word) {
|
||||||
|
return normalizeWord(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String firstCharOnlyToUpper(String word) {
|
||||||
|
return word.isEmpty()
|
||||||
|
? word
|
||||||
|
: Ascii.toUpperCase(word.charAt(0)) + Ascii.toLowerCase(word.substring(1));
|
||||||
|
}
|
||||||
|
}
|
1818
src/main/java/com/google/common/base/CharMatcher.java
Normal file
1818
src/main/java/com/google/common/base/CharMatcher.java
Normal file
File diff suppressed because it is too large
Load diff
102
src/main/java/com/google/common/base/Charsets.java
Normal file
102
src/main/java/com/google/common/base/Charsets.java
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains constant definitions for the six standard {@link Charset} instances, which are
|
||||||
|
* guaranteed to be supported by all Java platform implementations.
|
||||||
|
*
|
||||||
|
* <p>Assuming you're free to choose, note that <b>{@link #UTF_8} is widely preferred</b>.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/StringsExplained#charsets">{@code Charsets}</a>.
|
||||||
|
*
|
||||||
|
* @author Mike Bostock
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public final class Charsets {
|
||||||
|
private Charsets() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US).
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> this constant should be treated as deprecated; use {@link
|
||||||
|
* java.nio.charset.StandardCharsets#US_ASCII} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Charset not supported by GWT
|
||||||
|
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1).
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> this constant should be treated as deprecated; use {@link
|
||||||
|
* java.nio.charset.StandardCharsets#ISO_8859_1} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTF-8: eight-bit UCS Transformation Format.
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> this constant should be treated as deprecated; use {@link
|
||||||
|
* java.nio.charset.StandardCharsets#UTF_8} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order.
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> this constant should be treated as deprecated; use {@link
|
||||||
|
* java.nio.charset.StandardCharsets#UTF_16BE} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Charset not supported by GWT
|
||||||
|
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order.
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> this constant should be treated as deprecated; use {@link
|
||||||
|
* java.nio.charset.StandardCharsets#UTF_16LE} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Charset not supported by GWT
|
||||||
|
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order
|
||||||
|
* mark.
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> this constant should be treated as deprecated; use {@link
|
||||||
|
* java.nio.charset.StandardCharsets#UTF_16} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Charset not supported by GWT
|
||||||
|
public static final Charset UTF_16 = Charset.forName("UTF-16");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Please do not add new Charset references to this class, unless those character encodings are
|
||||||
|
* part of the set required to be supported by all Java platform implementations! Any Charsets
|
||||||
|
* initialized here may cause unexpected delays when this class is loaded. See the Charset
|
||||||
|
* Javadocs for the list of built-in character encodings.
|
||||||
|
*/
|
||||||
|
}
|
37
src/main/java/com/google/common/base/CommonMatcher.java
Normal file
37
src/main/java/com/google/common/base/CommonMatcher.java
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subset of the {@link java.util.regex.Matcher} API which is used by this package, and also
|
||||||
|
* shared with the {@code re2j} library. For internal use only. Please refer to the {@code Matcher}
|
||||||
|
* javadoc for details.
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class CommonMatcher {
|
||||||
|
public abstract boolean matches();
|
||||||
|
|
||||||
|
public abstract boolean find();
|
||||||
|
|
||||||
|
public abstract boolean find(int index);
|
||||||
|
|
||||||
|
public abstract String replaceAll(String replacement);
|
||||||
|
|
||||||
|
public abstract int end();
|
||||||
|
|
||||||
|
public abstract int start();
|
||||||
|
}
|
43
src/main/java/com/google/common/base/CommonPattern.java
Normal file
43
src/main/java/com/google/common/base/CommonPattern.java
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subset of the {@link java.util.regex.Pattern} API which is used by this package, and also
|
||||||
|
* shared with the {@code re2j} library. For internal use only. Please refer to the {@code Pattern}
|
||||||
|
* javadoc for details.
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class CommonPattern {
|
||||||
|
public abstract CommonMatcher matcher(CharSequence t);
|
||||||
|
|
||||||
|
public abstract String pattern();
|
||||||
|
|
||||||
|
public abstract int flags();
|
||||||
|
|
||||||
|
// Re-declare this as abstract to force subclasses to override.
|
||||||
|
@Override
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
|
public static CommonPattern compile(String pattern) {
|
||||||
|
return Platform.compilePattern(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPcreLike() {
|
||||||
|
return Platform.patternCompilerIsPcreLike();
|
||||||
|
}
|
||||||
|
}
|
506
src/main/java/com/google/common/base/Converter.java
Normal file
506
src/main/java/com/google/common/base/Converter.java
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function from {@code A} to {@code B} with an associated <i>reverse</i> function from {@code B}
|
||||||
|
* to {@code A}; used for converting back and forth between <i>different representations of the same
|
||||||
|
* information</i>.
|
||||||
|
*
|
||||||
|
* <h3>Invertibility</h3>
|
||||||
|
*
|
||||||
|
* <p>The reverse operation <b>may</b> be a strict <i>inverse</i> (meaning that {@code
|
||||||
|
* converter.reverse().convert(converter.convert(a)).equals(a)} is always true). However, it is very
|
||||||
|
* common (perhaps <i>more</i> common) for round-trip conversion to be <i>lossy</i>. Consider an
|
||||||
|
* example round-trip using {@link com.google.common.primitives.Doubles#stringConverter}:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>{@code stringConverter().convert("1.00")} returns the {@code Double} value {@code 1.0}
|
||||||
|
* <li>{@code stringConverter().reverse().convert(1.0)} returns the string {@code "1.0"} --
|
||||||
|
* <i>not</i> the same string ({@code "1.00"}) we started with
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>Note that it should still be the case that the round-tripped and original objects are
|
||||||
|
* <i>similar</i>.
|
||||||
|
*
|
||||||
|
* <h3>Nullability</h3>
|
||||||
|
*
|
||||||
|
* <p>A converter always converts {@code null} to {@code null} and non-null references to non-null
|
||||||
|
* references. It would not make sense to consider {@code null} and a non-null reference to be
|
||||||
|
* "different representations of the same information", since one is distinguishable from
|
||||||
|
* <i>missing</i> information and the other is not. The {@link #convert} method handles this null
|
||||||
|
* behavior for all converters; implementations of {@link #doForward} and {@link #doBackward} are
|
||||||
|
* guaranteed to never be passed {@code null}, and must never return {@code null}.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <h3>Common ways to use</h3>
|
||||||
|
*
|
||||||
|
* <p>Getting a converter:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Use a provided converter implementation, such as {@link Enums#stringConverter}, {@link
|
||||||
|
* com.google.common.primitives.Ints#stringConverter Ints.stringConverter} or the {@linkplain
|
||||||
|
* #reverse reverse} views of these.
|
||||||
|
* <li>Convert between specific preset values using {@link
|
||||||
|
* com.google.common.collect.Maps#asConverter Maps.asConverter}. For example, use this to
|
||||||
|
* create a "fake" converter for a unit test. It is unnecessary (and confusing) to <i>mock</i>
|
||||||
|
* the {@code Converter} type using a mocking framework.
|
||||||
|
* <li>Extend this class and implement its {@link #doForward} and {@link #doBackward} methods.
|
||||||
|
* <li><b>Java 8 users:</b> you may prefer to pass two lambda expressions or method references to
|
||||||
|
* the {@link #from from} factory method.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Using a converter:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Convert one instance in the "forward" direction using {@code converter.convert(a)}.
|
||||||
|
* <li>Convert multiple instances "forward" using {@code converter.convertAll(as)}.
|
||||||
|
* <li>Convert in the "backward" direction using {@code converter.reverse().convert(b)} or {@code
|
||||||
|
* converter.reverse().convertAll(bs)}.
|
||||||
|
* <li>Use {@code converter} or {@code converter.reverse()} anywhere a {@link
|
||||||
|
* java.util.function.Function} is accepted (for example {@link java.util.stream.Stream#map
|
||||||
|
* Stream.map}).
|
||||||
|
* <li><b>Do not</b> call {@link #doForward} or {@link #doBackward} directly; these exist only to
|
||||||
|
* be overridden.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h3>Example</h3>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* return new Converter<Integer, String>() {
|
||||||
|
* protected String doForward(Integer i) {
|
||||||
|
* return Integer.toHexString(i);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* protected Integer doBackward(String s) {
|
||||||
|
* return parseUnsignedInt(s, 16);
|
||||||
|
* }
|
||||||
|
* };</pre>
|
||||||
|
*
|
||||||
|
* <p>An alternative using Java 8:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* return Converter.from(
|
||||||
|
* Integer::toHexString,
|
||||||
|
* s -> parseUnsignedInt(s, 16));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Mike Ward
|
||||||
|
* @author Kurt Alfred Kluever
|
||||||
|
* @author Gregory Kick
|
||||||
|
* @since 16.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public abstract class Converter<A, B> implements Function<A, B> {
|
||||||
|
private final boolean handleNullAutomatically;
|
||||||
|
|
||||||
|
// We lazily cache the reverse view to avoid allocating on every call to reverse().
|
||||||
|
private transient Converter<B, A> reverse;
|
||||||
|
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected Converter() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor used only by {@code LegacyConverter} to suspend automatic null-handling. */
|
||||||
|
Converter(boolean handleNullAutomatically) {
|
||||||
|
this.handleNullAutomatically = handleNullAutomatically;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SPI methods (what subclasses must implement)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a representation of {@code a} as an instance of type {@code B}. If {@code a} cannot be
|
||||||
|
* converted, an unchecked exception (such as {@link IllegalArgumentException}) should be thrown.
|
||||||
|
*
|
||||||
|
* @param a the instance to convert; will never be null
|
||||||
|
* @return the converted instance; <b>must not</b> be null
|
||||||
|
*/
|
||||||
|
protected abstract B doForward(A a);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a representation of {@code b} as an instance of type {@code A}. If {@code b} cannot be
|
||||||
|
* converted, an unchecked exception (such as {@link IllegalArgumentException}) should be thrown.
|
||||||
|
*
|
||||||
|
* @param b the instance to convert; will never be null
|
||||||
|
* @return the converted instance; <b>must not</b> be null
|
||||||
|
* @throws UnsupportedOperationException if backward conversion is not implemented; this should be
|
||||||
|
* very rare. Note that if backward conversion is not only unimplemented but
|
||||||
|
* unimplement<i>able</i> (for example, consider a {@code Converter<Chicken, ChickenNugget>}),
|
||||||
|
* then this is not logically a {@code Converter} at all, and should just implement {@link
|
||||||
|
* Function}.
|
||||||
|
*/
|
||||||
|
protected abstract A doBackward(B b);
|
||||||
|
|
||||||
|
// API (consumer-side) methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a representation of {@code a} as an instance of type {@code B}.
|
||||||
|
*
|
||||||
|
* @return the converted value; is null <i>if and only if</i> {@code a} is null
|
||||||
|
*/
|
||||||
|
public final B convert(A a) {
|
||||||
|
return correctedDoForward(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
B correctedDoForward(A a) {
|
||||||
|
if (handleNullAutomatically) {
|
||||||
|
// TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert?
|
||||||
|
return a == null ? null : checkNotNull(doForward(a));
|
||||||
|
} else {
|
||||||
|
return doForward(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
A correctedDoBackward(B b) {
|
||||||
|
if (handleNullAutomatically) {
|
||||||
|
// TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert?
|
||||||
|
return b == null ? null : checkNotNull(doBackward(b));
|
||||||
|
} else {
|
||||||
|
return doBackward(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterable that applies {@code convert} to each element of {@code fromIterable}. The
|
||||||
|
* conversion is done lazily.
|
||||||
|
*
|
||||||
|
* <p>The returned iterable's iterator supports {@code remove()} if the input iterator does. After
|
||||||
|
* a successful {@code remove()} call, {@code fromIterable} no longer contains the corresponding
|
||||||
|
* element.
|
||||||
|
*/
|
||||||
|
public Iterable<B> convertAll(final Iterable<? extends A> fromIterable) {
|
||||||
|
checkNotNull(fromIterable, "fromIterable");
|
||||||
|
return new Iterable<B>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<B> iterator() {
|
||||||
|
return new Iterator<B>() {
|
||||||
|
private final Iterator<? extends A> fromIterator = fromIterable.iterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return fromIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public B next() {
|
||||||
|
return convert(fromIterator.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
fromIterator.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reversed view of this converter, which converts {@code this.convert(a)} back to a
|
||||||
|
* value roughly equivalent to {@code a}.
|
||||||
|
*
|
||||||
|
* <p>The returned converter is serializable if {@code this} converter is.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> you should not override this method. It is non-final for legacy reasons.
|
||||||
|
*/
|
||||||
|
public Converter<B, A> reverse() {
|
||||||
|
Converter<B, A> result = reverse;
|
||||||
|
return (result == null) ? reverse = new ReverseConverter<>(this) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ReverseConverter<A, B> extends Converter<B, A>
|
||||||
|
implements Serializable {
|
||||||
|
final Converter<A, B> original;
|
||||||
|
|
||||||
|
ReverseConverter(Converter<A, B> original) {
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These gymnastics are a little confusing. Basically this class has neither legacy nor
|
||||||
|
* non-legacy behavior; it just needs to let the behavior of the backing converter shine
|
||||||
|
* through. So, we override the correctedDo* methods, after which the do* methods should never
|
||||||
|
* be reached.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected A doForward(B b) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected B doBackward(A a) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
A correctedDoForward(B b) {
|
||||||
|
return original.correctedDoBackward(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
B correctedDoBackward(A a) {
|
||||||
|
return original.correctedDoForward(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Converter<A, B> reverse() {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof ReverseConverter) {
|
||||||
|
ReverseConverter<?, ?> that = (ReverseConverter<?, ?>) object;
|
||||||
|
return this.original.equals(that.original);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return ~original.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return original + ".reverse()";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a converter whose {@code convert} method applies {@code secondConverter} to the result
|
||||||
|
* of this converter. Its {@code reverse} method applies the converters in reverse order.
|
||||||
|
*
|
||||||
|
* <p>The returned converter is serializable if {@code this} converter and {@code secondConverter}
|
||||||
|
* are.
|
||||||
|
*/
|
||||||
|
public final <C> Converter<A, C> andThen(Converter<B, C> secondConverter) {
|
||||||
|
return doAndThen(secondConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Package-private non-final implementation of andThen() so only we can override it. */
|
||||||
|
<C> Converter<A, C> doAndThen(Converter<B, C> secondConverter) {
|
||||||
|
return new ConverterComposition<>(this, checkNotNull(secondConverter));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ConverterComposition<A, B, C> extends Converter<A, C>
|
||||||
|
implements Serializable {
|
||||||
|
final Converter<A, B> first;
|
||||||
|
final Converter<B, C> second;
|
||||||
|
|
||||||
|
ConverterComposition(Converter<A, B> first, Converter<B, C> second) {
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These gymnastics are a little confusing. Basically this class has neither legacy nor
|
||||||
|
* non-legacy behavior; it just needs to let the behaviors of the backing converters shine
|
||||||
|
* through (which might even differ from each other!). So, we override the correctedDo* methods,
|
||||||
|
* after which the do* methods should never be reached.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected C doForward(A a) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected A doBackward(C c) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
C correctedDoForward(A a) {
|
||||||
|
return second.correctedDoForward(first.correctedDoForward(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
A correctedDoBackward(C c) {
|
||||||
|
return first.correctedDoBackward(second.correctedDoBackward(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof ConverterComposition) {
|
||||||
|
ConverterComposition<?, ?, ?> that = (ConverterComposition<?, ?, ?>) object;
|
||||||
|
return this.first.equals(that.first) && this.second.equals(that.second);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * first.hashCode() + second.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return first + ".andThen(" + second + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Provided to satisfy the {@code Function} interface; use {@link #convert} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public final B apply(A a) {
|
||||||
|
return convert(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether another object is equal to this converter.
|
||||||
|
*
|
||||||
|
* <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.
|
||||||
|
* However, an implementation may also choose to return {@code true} whenever {@code object} is a
|
||||||
|
* {@link Converter} that it considers <i>interchangeable</i> with this one. "Interchangeable"
|
||||||
|
* <i>typically</i> means that {@code Objects.equal(this.convert(a), that.convert(a))} is true for
|
||||||
|
* all {@code a} of type {@code A} (and similarly for {@code reverse}). Note that a {@code false}
|
||||||
|
* result from this method does not imply that the converters are known <i>not</i> to be
|
||||||
|
* interchangeable.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
return super.equals(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static converters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a converter based on separate forward and backward functions. This is useful if the
|
||||||
|
* function instances already exist, or so that you can supply lambda expressions. If those
|
||||||
|
* circumstances don't apply, you probably don't need to use this; subclass {@code Converter} and
|
||||||
|
* implement its {@link #doForward} and {@link #doBackward} methods directly.
|
||||||
|
*
|
||||||
|
* <p>These functions will never be passed {@code null} and must not under any circumstances
|
||||||
|
* return {@code null}. If a value cannot be converted, the function should throw an unchecked
|
||||||
|
* exception (typically, but not necessarily, {@link IllegalArgumentException}).
|
||||||
|
*
|
||||||
|
* <p>The returned converter is serializable if both provided functions are.
|
||||||
|
*
|
||||||
|
* @since 17.0
|
||||||
|
*/
|
||||||
|
public static <A, B> Converter<A, B> from(
|
||||||
|
Function<? super A, ? extends B> forwardFunction,
|
||||||
|
Function<? super B, ? extends A> backwardFunction) {
|
||||||
|
return new FunctionBasedConverter<>(forwardFunction, backwardFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FunctionBasedConverter<A, B> extends Converter<A, B>
|
||||||
|
implements Serializable {
|
||||||
|
private final Function<? super A, ? extends B> forwardFunction;
|
||||||
|
private final Function<? super B, ? extends A> backwardFunction;
|
||||||
|
|
||||||
|
private FunctionBasedConverter(
|
||||||
|
Function<? super A, ? extends B> forwardFunction,
|
||||||
|
Function<? super B, ? extends A> backwardFunction) {
|
||||||
|
this.forwardFunction = checkNotNull(forwardFunction);
|
||||||
|
this.backwardFunction = checkNotNull(backwardFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected B doForward(A a) {
|
||||||
|
return forwardFunction.apply(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected A doBackward(B b) {
|
||||||
|
return backwardFunction.apply(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof FunctionBasedConverter) {
|
||||||
|
FunctionBasedConverter<?, ?> that = (FunctionBasedConverter<?, ?>) object;
|
||||||
|
return this.forwardFunction.equals(that.forwardFunction)
|
||||||
|
&& this.backwardFunction.equals(that.backwardFunction);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return forwardFunction.hashCode() * 31 + backwardFunction.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Converter.from(" + forwardFunction + ", " + backwardFunction + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a serializable converter that always converts or reverses an object to itself. */
|
||||||
|
@SuppressWarnings("unchecked") // implementation is "fully variant"
|
||||||
|
public static <T> Converter<T, T> identity() {
|
||||||
|
return (IdentityConverter<T>) IdentityConverter.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A converter that always converts or reverses an object to itself. Note that T is now a
|
||||||
|
* "pass-through type".
|
||||||
|
*/
|
||||||
|
private static final class IdentityConverter<T> extends Converter<T, T> implements Serializable {
|
||||||
|
static final IdentityConverter<?> INSTANCE = new IdentityConverter<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T doForward(T t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T doBackward(T t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityConverter<T> reverse() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<S> Converter<T, S> doAndThen(Converter<T, S> otherConverter) {
|
||||||
|
return checkNotNull(otherConverter, "otherConverter");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We *could* override convertAll() to return its input, but it's a rather pointless
|
||||||
|
* optimization and opened up a weird type-safety problem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Converter.identity()";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResolve() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
}
|
||||||
|
}
|
62
src/main/java/com/google/common/base/Defaults.java
Normal file
62
src/main/java/com/google/common/base/Defaults.java
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides default values for all Java types, as defined by the JLS.
|
||||||
|
*
|
||||||
|
* @author Ben Yu
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public final class Defaults {
|
||||||
|
private Defaults() {}
|
||||||
|
|
||||||
|
private static final Double DOUBLE_DEFAULT = Double.valueOf(0d);
|
||||||
|
private static final Float FLOAT_DEFAULT = Float.valueOf(0f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default value of {@code type} as defined by JLS --- {@code 0} for numbers, {@code
|
||||||
|
* false} for {@code boolean} and {@code '\0'} for {@code char}. For non-primitive types and
|
||||||
|
* {@code void}, {@code null} is returned.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T defaultValue(Class<T> type) {
|
||||||
|
checkNotNull(type);
|
||||||
|
if (type == boolean.class) {
|
||||||
|
return (T) Boolean.FALSE;
|
||||||
|
} else if (type == char.class) {
|
||||||
|
return (T) Character.valueOf('\0');
|
||||||
|
} else if (type == byte.class) {
|
||||||
|
return (T) Byte.valueOf((byte) 0);
|
||||||
|
} else if (type == short.class) {
|
||||||
|
return (T) Short.valueOf((short) 0);
|
||||||
|
} else if (type == int.class) {
|
||||||
|
return (T) Integer.valueOf(0);
|
||||||
|
} else if (type == long.class) {
|
||||||
|
return (T) Long.valueOf(0L);
|
||||||
|
} else if (type == float.class) {
|
||||||
|
return (T) FLOAT_DEFAULT;
|
||||||
|
} else if (type == double.class) {
|
||||||
|
return (T) DOUBLE_DEFAULT;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
src/main/java/com/google/common/base/Enums.java
Normal file
150
src/main/java/com/google/common/base/Enums.java
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for working with {@link Enum} instances.
|
||||||
|
*
|
||||||
|
* @author Steve McKay
|
||||||
|
* @since 9.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public final class Enums {
|
||||||
|
|
||||||
|
private Enums() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Field} in which {@code enumValue} is defined. For example, to get the {@code
|
||||||
|
* Description} annotation on the {@code GOLF} constant of enum {@code Sport}, use {@code
|
||||||
|
* Enums.getField(Sport.GOLF).getAnnotation(Description.class)}.
|
||||||
|
*
|
||||||
|
* @since 12.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // reflection
|
||||||
|
public static Field getField(Enum<?> enumValue) {
|
||||||
|
Class<?> clazz = enumValue.getDeclaringClass();
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(enumValue.name());
|
||||||
|
} catch (NoSuchFieldException impossible) {
|
||||||
|
throw new AssertionError(impossible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an optional enum constant for the given type, using {@link Enum#valueOf}. If the
|
||||||
|
* constant does not exist, {@link Optional#absent} is returned. A common use case is for parsing
|
||||||
|
* user input or falling back to a default enum constant. For example, {@code
|
||||||
|
* Enums.getIfPresent(Country.class, countryInput).or(Country.DEFAULT);}
|
||||||
|
*
|
||||||
|
* @since 12.0
|
||||||
|
*/
|
||||||
|
public static <T extends Enum<T>> Optional<T> getIfPresent(Class<T> enumClass, String value) {
|
||||||
|
checkNotNull(enumClass);
|
||||||
|
checkNotNull(value);
|
||||||
|
return Platform.getEnumIfPresent(enumClass, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // java.lang.ref.WeakReference
|
||||||
|
private static final Map<Class<? extends Enum<?>>, Map<String, WeakReference<? extends Enum<?>>>>
|
||||||
|
enumConstantCache = new WeakHashMap<>();
|
||||||
|
|
||||||
|
@GwtIncompatible // java.lang.ref.WeakReference
|
||||||
|
private static <T extends Enum<T>> Map<String, WeakReference<? extends Enum<?>>> populateCache(
|
||||||
|
Class<T> enumClass) {
|
||||||
|
Map<String, WeakReference<? extends Enum<?>>> result = new HashMap<>();
|
||||||
|
for (T enumInstance : EnumSet.allOf(enumClass)) {
|
||||||
|
result.put(enumInstance.name(), new WeakReference<Enum<?>>(enumInstance));
|
||||||
|
}
|
||||||
|
enumConstantCache.put(enumClass, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // java.lang.ref.WeakReference
|
||||||
|
static <T extends Enum<T>> Map<String, WeakReference<? extends Enum<?>>> getEnumConstants(
|
||||||
|
Class<T> enumClass) {
|
||||||
|
synchronized (enumConstantCache) {
|
||||||
|
Map<String, WeakReference<? extends Enum<?>>> constants = enumConstantCache.get(enumClass);
|
||||||
|
if (constants == null) {
|
||||||
|
constants = populateCache(enumClass);
|
||||||
|
}
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a converter that converts between strings and {@code enum} values of type {@code
|
||||||
|
* enumClass} using {@link Enum#valueOf(Class, String)} and {@link Enum#name()}. The converter
|
||||||
|
* will throw an {@code IllegalArgumentException} if the argument is not the name of any enum
|
||||||
|
* constant in the specified enum.
|
||||||
|
*
|
||||||
|
* @since 16.0
|
||||||
|
*/
|
||||||
|
public static <T extends Enum<T>> Converter<String, T> stringConverter(final Class<T> enumClass) {
|
||||||
|
return new StringConverter<T>(enumClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class StringConverter<T extends Enum<T>> extends Converter<String, T>
|
||||||
|
implements Serializable {
|
||||||
|
|
||||||
|
private final Class<T> enumClass;
|
||||||
|
|
||||||
|
StringConverter(Class<T> enumClass) {
|
||||||
|
this.enumClass = checkNotNull(enumClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T doForward(String value) {
|
||||||
|
return Enum.valueOf(enumClass, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doBackward(T enumValue) {
|
||||||
|
return enumValue.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof StringConverter) {
|
||||||
|
StringConverter<?> that = (StringConverter<?>) object;
|
||||||
|
return this.enumClass.equals(that.enumClass);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return enumClass.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Enums.stringConverter(" + enumClass.getName() + ".class)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
}
|
||||||
|
}
|
374
src/main/java/com/google/common/base/Equivalence.java
Normal file
374
src/main/java/com/google/common/base/Equivalence.java
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A strategy for determining whether two instances are considered equivalent, and for computing
|
||||||
|
* hash codes in a manner consistent with that equivalence. Two examples of equivalences are the
|
||||||
|
* {@linkplain #identity() identity equivalence} and the {@linkplain #equals "equals" equivalence}.
|
||||||
|
*
|
||||||
|
* @author Bob Lee
|
||||||
|
* @author Ben Yu
|
||||||
|
* @author Gregory Kick
|
||||||
|
* @since 10.0 (<a href="https://github.com/google/guava/wiki/Compatibility">mostly
|
||||||
|
* source-compatible</a> since 4.0)
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public abstract class Equivalence<T> implements BiPredicate<T, T> {
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected Equivalence() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the given objects are considered equivalent.
|
||||||
|
*
|
||||||
|
* <p>This method describes an <i>equivalence relation</i> on object references, meaning that for
|
||||||
|
* all references {@code x}, {@code y}, and {@code z} (any of which may be null):
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code equivalent(x, x)} is true (<i>reflexive</i> property)
|
||||||
|
* <li>{@code equivalent(x, y)} and {@code equivalent(y, x)} each return the same result
|
||||||
|
* (<i>symmetric</i> property)
|
||||||
|
* <li>If {@code equivalent(x, y)} and {@code equivalent(y, z)} are both true, then {@code
|
||||||
|
* equivalent(x, z)} is also true (<i>transitive</i> property)
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Note that all calls to {@code equivalent(x, y)} are expected to return the same result as
|
||||||
|
* long as neither {@code x} nor {@code y} is modified.
|
||||||
|
*/
|
||||||
|
public final boolean equivalent(T a, T b) {
|
||||||
|
if (a == b) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a == null || b == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return doEquivalent(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Provided only to satisfy the {@link BiPredicate} interface; use {@link #equivalent}
|
||||||
|
* instead.
|
||||||
|
* @since 21.0
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public final boolean test(T t, T u) {
|
||||||
|
return equivalent(t, u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemented by the user to determine whether {@code a} and {@code b} are considered equivalent,
|
||||||
|
* subject to the requirements specified in {@link #equivalent}.
|
||||||
|
*
|
||||||
|
* <p>This method should not be called except by {@link #equivalent}. When {@link #equivalent}
|
||||||
|
* calls this method, {@code a} and {@code b} are guaranteed to be distinct, non-null instances.
|
||||||
|
*
|
||||||
|
* @since 10.0 (previously, subclasses would override equivalent())
|
||||||
|
*/
|
||||||
|
protected abstract boolean doEquivalent(T a, T b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code for {@code t}.
|
||||||
|
*
|
||||||
|
* <p>The {@code hash} has the following properties:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>It is <i>consistent</i>: for any reference {@code x}, multiple invocations of {@code
|
||||||
|
* hash(x}} consistently return the same value provided {@code x} remains unchanged
|
||||||
|
* according to the definition of the equivalence. The hash need not remain consistent from
|
||||||
|
* one execution of an application to another execution of the same application.
|
||||||
|
* <li>It is <i>distributable across equivalence</i>: for any references {@code x} and {@code
|
||||||
|
* y}, if {@code equivalent(x, y)}, then {@code hash(x) == hash(y)}. It is <i>not</i>
|
||||||
|
* necessary that the hash be distributable across <i>inequivalence</i>. If {@code
|
||||||
|
* equivalence(x, y)} is false, {@code hash(x) == hash(y)} may still be true.
|
||||||
|
* <li>{@code hash(null)} is {@code 0}.
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final int hash(T t) {
|
||||||
|
if (t == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return doHash(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemented by the user to return a hash code for {@code t}, subject to the requirements
|
||||||
|
* specified in {@link #hash}.
|
||||||
|
*
|
||||||
|
* <p>This method should not be called except by {@link #hash}. When {@link #hash} calls this
|
||||||
|
* method, {@code t} is guaranteed to be non-null.
|
||||||
|
*
|
||||||
|
* @since 10.0 (previously, subclasses would override hash())
|
||||||
|
*/
|
||||||
|
protected abstract int doHash(T t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new equivalence relation for {@code F} which evaluates equivalence by first applying
|
||||||
|
* {@code function} to the argument, then evaluating using {@code this}. That is, for any pair of
|
||||||
|
* non-null objects {@code x} and {@code y}, {@code equivalence.onResultOf(function).equivalent(a,
|
||||||
|
* b)} is true if and only if {@code equivalence.equivalent(function.apply(a), function.apply(b))}
|
||||||
|
* is true.
|
||||||
|
*
|
||||||
|
* <p>For example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Equivalence<Person> SAME_AGE = Equivalence.equals().onResultOf(GET_PERSON_AGE);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>{@code function} will never be invoked with a null value.
|
||||||
|
*
|
||||||
|
* <p>Note that {@code function} must be consistent according to {@code this} equivalence
|
||||||
|
* relation. That is, invoking {@link Function#apply} multiple times for a given value must return
|
||||||
|
* equivalent results. For example, {@code
|
||||||
|
* Equivalence.identity().onResultOf(Functions.toStringFunction())} is broken because it's not
|
||||||
|
* guaranteed that {@link Object#toString}) always returns the same string instance.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public final <F> Equivalence<F> onResultOf(Function<F, ? extends T> function) {
|
||||||
|
return new FunctionalEquivalence<>(function, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a wrapper of {@code reference} that implements {@link Wrapper#equals(Object)
|
||||||
|
* Object.equals()} such that {@code wrap(a).equals(wrap(b))} if and only if {@code equivalent(a,
|
||||||
|
* b)}.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public final <S extends T> Wrapper<S> wrap(S reference) {
|
||||||
|
return new Wrapper<S>(this, reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an object so that {@link #equals(Object)} and {@link #hashCode()} delegate to an {@link
|
||||||
|
* Equivalence}.
|
||||||
|
*
|
||||||
|
* <p>For example, given an {@link Equivalence} for {@link String strings} named {@code equiv}
|
||||||
|
* that tests equivalence using their lengths:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* equiv.wrap("a").equals(equiv.wrap("b")) // true
|
||||||
|
* equiv.wrap("a").equals(equiv.wrap("hello")) // false
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Note in particular that an equivalence wrapper is never equal to the object it wraps.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* equiv.wrap(obj).equals(obj) // always false
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public static final class Wrapper<T> implements Serializable {
|
||||||
|
private final Equivalence<? super T> equivalence;
|
||||||
|
private final T reference;
|
||||||
|
|
||||||
|
private Wrapper(Equivalence<? super T> equivalence, T reference) {
|
||||||
|
this.equivalence = checkNotNull(equivalence);
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the (possibly null) reference wrapped by this instance. */
|
||||||
|
public T get() {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if {@link Equivalence#equivalent(Object, Object)} applied to the wrapped
|
||||||
|
* references is {@code true} and both wrappers use the {@link Object#equals(Object) same}
|
||||||
|
* equivalence.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof Wrapper) {
|
||||||
|
Wrapper<?> that = (Wrapper<?>) obj; // note: not necessarily a Wrapper<T>
|
||||||
|
|
||||||
|
if (this.equivalence.equals(that.equivalence)) {
|
||||||
|
/*
|
||||||
|
* We'll accept that as sufficient "proof" that either equivalence should be able to
|
||||||
|
* handle either reference, so it's safe to circumvent compile-time type checking.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Equivalence<Object> equivalence = (Equivalence<Object>) this.equivalence;
|
||||||
|
return equivalence.equivalent(this.reference, that.reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the result of {@link Equivalence#hash(Object)} applied to the wrapped reference. */
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return equivalence.hash(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation for this equivalence wrapper. The form of this string
|
||||||
|
* representation is not specified.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return equivalence + ".wrap(" + reference + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an equivalence over iterables based on the equivalence of their elements. More
|
||||||
|
* specifically, two iterables are considered equivalent if they both contain the same number of
|
||||||
|
* elements, and each pair of corresponding elements is equivalent according to {@code this}. Null
|
||||||
|
* iterables are equivalent to one another.
|
||||||
|
*
|
||||||
|
* <p>Note that this method performs a similar function for equivalences as {@link
|
||||||
|
* com.google.common.collect.Ordering#lexicographical} does for orderings.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
public final <S extends T> Equivalence<Iterable<S>> pairwise() {
|
||||||
|
// Ideally, the returned equivalence would support Iterable<? extends T>. However,
|
||||||
|
// the need for this is so rare that it's not worth making callers deal with the ugly wildcard.
|
||||||
|
return new PairwiseEquivalence<S>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to true if and only if the input is equivalent to {@code
|
||||||
|
* target} according to this equivalence relation.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public final Predicate<T> equivalentTo(T target) {
|
||||||
|
return new EquivalentToPredicate<T>(this, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class EquivalentToPredicate<T> implements Predicate<T>, Serializable {
|
||||||
|
|
||||||
|
private final Equivalence<T> equivalence;
|
||||||
|
private final T target;
|
||||||
|
|
||||||
|
EquivalentToPredicate(Equivalence<T> equivalence, T target) {
|
||||||
|
this.equivalence = checkNotNull(equivalence);
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(T input) {
|
||||||
|
return equivalence.equivalent(input, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof EquivalentToPredicate) {
|
||||||
|
EquivalentToPredicate<?> that = (EquivalentToPredicate<?>) obj;
|
||||||
|
return equivalence.equals(that.equivalence) && Objects.equal(target, that.target);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(equivalence, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return equivalence + ".equivalentTo(" + target + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an equivalence that delegates to {@link Object#equals} and {@link Object#hashCode}.
|
||||||
|
* {@link Equivalence#equivalent} returns {@code true} if both values are null, or if neither
|
||||||
|
* value is null and {@link Object#equals} returns {@code true}. {@link Equivalence#hash} returns
|
||||||
|
* {@code 0} if passed a null value.
|
||||||
|
*
|
||||||
|
* @since 13.0
|
||||||
|
* @since 8.0 (in Equivalences with null-friendly behavior)
|
||||||
|
* @since 4.0 (in Equivalences)
|
||||||
|
*/
|
||||||
|
public static Equivalence<Object> equals() {
|
||||||
|
return Equals.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an equivalence that uses {@code ==} to compare values and {@link
|
||||||
|
* System#identityHashCode(Object)} to compute the hash code. {@link Equivalence#equivalent}
|
||||||
|
* returns {@code true} if {@code a == b}, including in the case that a and b are both null.
|
||||||
|
*
|
||||||
|
* @since 13.0
|
||||||
|
* @since 4.0 (in Equivalences)
|
||||||
|
*/
|
||||||
|
public static Equivalence<Object> identity() {
|
||||||
|
return Identity.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Equals extends Equivalence<Object> implements Serializable {
|
||||||
|
|
||||||
|
static final Equals INSTANCE = new Equals();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doEquivalent(Object a, Object b) {
|
||||||
|
return a.equals(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doHash(Object o) {
|
||||||
|
return o.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResolve() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Identity extends Equivalence<Object> implements Serializable {
|
||||||
|
|
||||||
|
static final Identity INSTANCE = new Identity();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doEquivalent(Object a, Object b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doHash(Object o) {
|
||||||
|
return System.identityHashCode(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResolve() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular
|
||||||
|
* version.
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
abstract class ExtraObjectsMethodsForWeb {}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.lang.ref.PhantomReference;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phantom reference with a {@code finalizeReferent()} method which a background thread invokes
|
||||||
|
* after the garbage collector reclaims the referent. This is a simpler alternative to using a
|
||||||
|
* {@link ReferenceQueue}.
|
||||||
|
*
|
||||||
|
* <p>Unlike a normal phantom reference, this reference will be cleared automatically.
|
||||||
|
*
|
||||||
|
* @author Bob Lee
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public abstract class FinalizablePhantomReference<T> extends PhantomReference<T>
|
||||||
|
implements FinalizableReference {
|
||||||
|
/**
|
||||||
|
* Constructs a new finalizable phantom reference.
|
||||||
|
*
|
||||||
|
* @param referent to phantom reference
|
||||||
|
* @param queue that should finalize the referent
|
||||||
|
*/
|
||||||
|
protected FinalizablePhantomReference(T referent, FinalizableReferenceQueue queue) {
|
||||||
|
super(referent, queue.queue);
|
||||||
|
queue.cleanUp();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemented by references that have code to run after garbage collection of their referents.
|
||||||
|
*
|
||||||
|
* @see FinalizableReferenceQueue
|
||||||
|
* @author Bob Lee
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public interface FinalizableReference {
|
||||||
|
/**
|
||||||
|
* Invoked on a background thread after the referent has been garbage collected unless security
|
||||||
|
* restrictions prevented starting a background thread, in which case this method is invoked when
|
||||||
|
* new references are created.
|
||||||
|
*/
|
||||||
|
void finalizeReferent();
|
||||||
|
}
|
|
@ -0,0 +1,350 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.PhantomReference;
|
||||||
|
import java.lang.ref.Reference;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference queue with an associated background thread that dequeues references and invokes
|
||||||
|
* {@link FinalizableReference#finalizeReferent()} on them.
|
||||||
|
*
|
||||||
|
* <p>Keep a strong reference to this object until all of the associated referents have been
|
||||||
|
* finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code
|
||||||
|
* finalizeReferent()} on the remaining references.
|
||||||
|
*
|
||||||
|
* <p>As an example of how this is used, imagine you have a class {@code MyServer} that creates a a
|
||||||
|
* {@link java.net.ServerSocket ServerSocket}, and you would like to ensure that the {@code
|
||||||
|
* ServerSocket} is closed even if the {@code MyServer} object is garbage-collected without calling
|
||||||
|
* its {@code close} method. You <em>could</em> use a finalizer to accomplish this, but that has a
|
||||||
|
* number of well-known problems. Here is how you might use this class instead:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* public class MyServer implements Closeable {
|
||||||
|
* private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue();
|
||||||
|
* // You might also share this between several objects.
|
||||||
|
*
|
||||||
|
* private static final Set<Reference<?>> references = Sets.newConcurrentHashSet();
|
||||||
|
* // This ensures that the FinalizablePhantomReference itself is not garbage-collected.
|
||||||
|
*
|
||||||
|
* private final ServerSocket serverSocket;
|
||||||
|
*
|
||||||
|
* private MyServer(...) {
|
||||||
|
* ...
|
||||||
|
* this.serverSocket = new ServerSocket(...);
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public static MyServer create(...) {
|
||||||
|
* MyServer myServer = new MyServer(...);
|
||||||
|
* final ServerSocket serverSocket = myServer.serverSocket;
|
||||||
|
* Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) {
|
||||||
|
* public void finalizeReferent() {
|
||||||
|
* references.remove(this):
|
||||||
|
* if (!serverSocket.isClosed()) {
|
||||||
|
* ...log a message about how nobody called close()...
|
||||||
|
* try {
|
||||||
|
* serverSocket.close();
|
||||||
|
* } catch (IOException e) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* references.add(reference);
|
||||||
|
* return myServer;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public void close() {
|
||||||
|
* serverSocket.close();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Bob Lee
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public class FinalizableReferenceQueue implements Closeable {
|
||||||
|
/*
|
||||||
|
* The Finalizer thread keeps a phantom reference to this object. When the client (for example, a
|
||||||
|
* map built by MapMaker) no longer has a strong reference to this object, the garbage collector
|
||||||
|
* will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the
|
||||||
|
* Finalizer to stop.
|
||||||
|
*
|
||||||
|
* If this library is loaded in the system class loader, FinalizableReferenceQueue can load
|
||||||
|
* Finalizer directly with no problems.
|
||||||
|
*
|
||||||
|
* If this library is loaded in an application class loader, it's important that Finalizer not
|
||||||
|
* have a strong reference back to the class loader. Otherwise, you could have a graph like this:
|
||||||
|
*
|
||||||
|
* Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader
|
||||||
|
* which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance
|
||||||
|
*
|
||||||
|
* Even if no other references to classes from the application class loader remain, the Finalizer
|
||||||
|
* thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the
|
||||||
|
* Finalizer running, and as a result, the application class loader can never be reclaimed.
|
||||||
|
*
|
||||||
|
* This means that dynamically loaded web applications and OSGi bundles can't be unloaded.
|
||||||
|
*
|
||||||
|
* If the library is loaded in an application class loader, we try to break the cycle by loading
|
||||||
|
* Finalizer in its own independent class loader:
|
||||||
|
*
|
||||||
|
* System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue ->
|
||||||
|
* etc. -> Decoupled class loader -> Finalizer
|
||||||
|
*
|
||||||
|
* Now, Finalizer no longer keeps an indirect strong reference to the static
|
||||||
|
* FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed
|
||||||
|
* at which point the Finalizer thread will stop and its decoupled class loader can also be
|
||||||
|
* reclaimed.
|
||||||
|
*
|
||||||
|
* If any of this fails along the way, we fall back to loading Finalizer directly in the
|
||||||
|
* application class loader.
|
||||||
|
*
|
||||||
|
* NOTE: The tests for this behavior (FinalizableReferenceQueueClassLoaderUnloadingTest) fail
|
||||||
|
* strangely when run in JDK 9. We are considering this a known issue. Please see
|
||||||
|
* https://github.com/google/guava/issues/3086 for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName());
|
||||||
|
|
||||||
|
private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
|
||||||
|
|
||||||
|
/** Reference to Finalizer.startFinalizer(). */
|
||||||
|
private static final Method startFinalizer;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Class<?> finalizer =
|
||||||
|
loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader());
|
||||||
|
startFinalizer = getStartFinalizer(finalizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The actual reference queue that our background thread will poll. */
|
||||||
|
final ReferenceQueue<Object> queue;
|
||||||
|
|
||||||
|
final PhantomReference<Object> frqRef;
|
||||||
|
|
||||||
|
/** Whether or not the background thread started successfully. */
|
||||||
|
final boolean threadStarted;
|
||||||
|
|
||||||
|
/** Constructs a new queue. */
|
||||||
|
public FinalizableReferenceQueue() {
|
||||||
|
// We could start the finalizer lazily, but I'd rather it blow up early.
|
||||||
|
queue = new ReferenceQueue<>();
|
||||||
|
frqRef = new PhantomReference<Object>(this, queue);
|
||||||
|
boolean threadStarted = false;
|
||||||
|
try {
|
||||||
|
startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef);
|
||||||
|
threadStarted = true;
|
||||||
|
} catch (IllegalAccessException impossible) {
|
||||||
|
throw new AssertionError(impossible); // startFinalizer() is public
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(
|
||||||
|
Level.INFO,
|
||||||
|
"Failed to start reference finalizer thread."
|
||||||
|
+ " Reference cleanup will only occur when new references are created.",
|
||||||
|
t);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.threadStarted = threadStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
frqRef.enqueue();
|
||||||
|
cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeatedly dequeues references from the queue and invokes {@link
|
||||||
|
* FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a
|
||||||
|
* no-op if the background thread was created successfully.
|
||||||
|
*/
|
||||||
|
void cleanUp() {
|
||||||
|
if (threadStarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reference<?> reference;
|
||||||
|
while ((reference = queue.poll()) != null) {
|
||||||
|
/*
|
||||||
|
* This is for the benefit of phantom references. Weak and soft references will have already
|
||||||
|
* been cleared by this point.
|
||||||
|
*/
|
||||||
|
reference.clear();
|
||||||
|
try {
|
||||||
|
((FinalizableReference) reference).finalizeReferent();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the given loaders until it finds one that can load Finalizer.
|
||||||
|
*
|
||||||
|
* @return Finalizer.class
|
||||||
|
*/
|
||||||
|
private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
|
||||||
|
for (FinalizerLoader loader : loaders) {
|
||||||
|
Class<?> finalizer = loader.loadFinalizer();
|
||||||
|
if (finalizer != null) {
|
||||||
|
return finalizer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads Finalizer.class. */
|
||||||
|
interface FinalizerLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Finalizer.class or null if this loader shouldn't or can't load it.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if we don't have the appropriate privileges
|
||||||
|
*/
|
||||||
|
Class<?> loadFinalizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to load Finalizer from the system class loader. If Finalizer is in the system class path,
|
||||||
|
* we needn't create a separate loader.
|
||||||
|
*/
|
||||||
|
static class SystemLoader implements FinalizerLoader {
|
||||||
|
// This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable
|
||||||
|
// finding Finalizer on the system class path even if it is there.
|
||||||
|
@VisibleForTesting static boolean disabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> loadFinalizer() {
|
||||||
|
if (disabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ClassLoader systemLoader;
|
||||||
|
try {
|
||||||
|
systemLoader = ClassLoader.getSystemClassLoader();
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
logger.info("Not allowed to access system class loader.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (systemLoader != null) {
|
||||||
|
try {
|
||||||
|
return systemLoader.loadClass(FINALIZER_CLASS_NAME);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Ignore. Finalizer is simply in a child class loader.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to
|
||||||
|
* our class loader (which could be that of a dynamically loaded web application or OSGi bundle),
|
||||||
|
* it would prevent our class loader from getting garbage collected.
|
||||||
|
*/
|
||||||
|
static class DecoupledLoader implements FinalizerLoader {
|
||||||
|
private static final String LOADING_ERROR =
|
||||||
|
"Could not load Finalizer in its own class loader. Loading Finalizer in the current class "
|
||||||
|
+ "loader instead. As a result, you will not be able to garbage collect this class "
|
||||||
|
+ "loader. To support reclaiming this class loader, either resolve the underlying "
|
||||||
|
+ "issue, or move Guava to your system class path.";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> loadFinalizer() {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* We use URLClassLoader because it's the only concrete class loader implementation in the
|
||||||
|
* JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this
|
||||||
|
* class loader:
|
||||||
|
*
|
||||||
|
* Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader
|
||||||
|
*
|
||||||
|
* System class loader will (and must) be the parent.
|
||||||
|
*/
|
||||||
|
ClassLoader finalizerLoader = newLoader(getBaseUrl());
|
||||||
|
return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, LOADING_ERROR, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets URL for base of path containing Finalizer.class. */
|
||||||
|
URL getBaseUrl() throws IOException {
|
||||||
|
// Find URL pointing to Finalizer.class file.
|
||||||
|
String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
|
||||||
|
URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
|
||||||
|
if (finalizerUrl == null) {
|
||||||
|
throw new FileNotFoundException(finalizerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find URL pointing to base of class path.
|
||||||
|
String urlString = finalizerUrl.toString();
|
||||||
|
if (!urlString.endsWith(finalizerPath)) {
|
||||||
|
throw new IOException("Unsupported path style: " + urlString);
|
||||||
|
}
|
||||||
|
urlString = urlString.substring(0, urlString.length() - finalizerPath.length());
|
||||||
|
return new URL(finalizerUrl, urlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a class loader with the given base URL as its classpath. */
|
||||||
|
URLClassLoader newLoader(URL base) {
|
||||||
|
// We use the bootstrap class loader as the parent because Finalizer by design uses
|
||||||
|
// only standard Java classes. That also means that FinalizableReferenceQueueTest
|
||||||
|
// doesn't pick up the wrong version of the Finalizer class.
|
||||||
|
return new URLClassLoader(new URL[] {base}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads Finalizer directly using the current class loader. We won't be able to garbage collect
|
||||||
|
* this class loader, but at least the world doesn't end.
|
||||||
|
*/
|
||||||
|
static class DirectLoader implements FinalizerLoader {
|
||||||
|
@Override
|
||||||
|
public Class<?> loadFinalizer() {
|
||||||
|
try {
|
||||||
|
return Class.forName(FINALIZER_CLASS_NAME);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Looks up Finalizer.startFinalizer(). */
|
||||||
|
static Method getStartFinalizer(Class<?> finalizer) {
|
||||||
|
try {
|
||||||
|
return finalizer.getMethod(
|
||||||
|
"startFinalizer", Class.class, ReferenceQueue.class, PhantomReference.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soft reference with a {@code finalizeReferent()} method which a background thread invokes after
|
||||||
|
* the garbage collector reclaims the referent. This is a simpler alternative to using a {@link
|
||||||
|
* ReferenceQueue}.
|
||||||
|
*
|
||||||
|
* @author Bob Lee
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public abstract class FinalizableSoftReference<T> extends SoftReference<T>
|
||||||
|
implements FinalizableReference {
|
||||||
|
/**
|
||||||
|
* Constructs a new finalizable soft reference.
|
||||||
|
*
|
||||||
|
* @param referent to softly reference
|
||||||
|
* @param queue that should finalize the referent
|
||||||
|
*/
|
||||||
|
protected FinalizableSoftReference(T referent, FinalizableReferenceQueue queue) {
|
||||||
|
super(referent, queue.queue);
|
||||||
|
queue.cleanUp();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weak reference with a {@code finalizeReferent()} method which a background thread invokes after
|
||||||
|
* the garbage collector reclaims the referent. This is a simpler alternative to using a {@link
|
||||||
|
* ReferenceQueue}.
|
||||||
|
*
|
||||||
|
* @author Bob Lee
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public abstract class FinalizableWeakReference<T> extends WeakReference<T>
|
||||||
|
implements FinalizableReference {
|
||||||
|
/**
|
||||||
|
* Constructs a new finalizable weak reference.
|
||||||
|
*
|
||||||
|
* @param referent to weakly reference
|
||||||
|
* @param queue that should finalize the referent
|
||||||
|
*/
|
||||||
|
protected FinalizableWeakReference(T referent, FinalizableReferenceQueue queue) {
|
||||||
|
super(referent, queue.queue);
|
||||||
|
queue.cleanUp();
|
||||||
|
}
|
||||||
|
}
|
59
src/main/java/com/google/common/base/Function.java
Normal file
59
src/main/java/com/google/common/base/Function.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy version of {@link java.util.function.Function java.util.function.Function}.
|
||||||
|
*
|
||||||
|
* <p>The {@link Functions} class provides common functions and related utilities.
|
||||||
|
*
|
||||||
|
* <p>As this interface extends {@code java.util.function.Function}, an instance of this type can be
|
||||||
|
* used as a {@code java.util.function.Function} directly. To use a {@code
|
||||||
|
* java.util.function.Function} in a context where a {@code com.google.common.base.Function} is
|
||||||
|
* needed, use {@code function::apply}.
|
||||||
|
*
|
||||||
|
* <p>This interface is now a legacy type. Use {@code java.util.function.Function} (or the
|
||||||
|
* appropriate primitive specialization such as {@code ToIntFunction}) instead whenever possible.
|
||||||
|
* Otherwise, at least reduce <i>explicit</i> dependencies on this type by using lambda expressions
|
||||||
|
* or method references instead of classes, leaving your code easier to migrate in the future.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/FunctionalExplained">the use of {@code Function}</a>.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Function<F, T> extends java.util.function.Function<F, T> {
|
||||||
|
@Override
|
||||||
|
T apply(F input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <i>May</i> return {@code true} if {@code object} is a {@code Function} that behaves identically
|
||||||
|
* to this function.
|
||||||
|
*
|
||||||
|
* <p><b>Warning: do not depend</b> on the behavior of this method.
|
||||||
|
*
|
||||||
|
* <p>Historically, {@code Function} instances in this library have implemented this method to
|
||||||
|
* recognize certain cases where distinct {@code Function} instances would in fact behave
|
||||||
|
* identically. However, as code migrates to {@code java.util.function}, that behavior will
|
||||||
|
* disappear. It is best not to depend on it.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
boolean equals(Object object);
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalence applied on functional result.
|
||||||
|
*
|
||||||
|
* @author Bob Lee
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
@GwtCompatible
|
||||||
|
final class FunctionalEquivalence<F, T> extends Equivalence<F> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
|
||||||
|
private final Function<F, ? extends T> function;
|
||||||
|
private final Equivalence<T> resultEquivalence;
|
||||||
|
|
||||||
|
FunctionalEquivalence(Function<F, ? extends T> function, Equivalence<T> resultEquivalence) {
|
||||||
|
this.function = checkNotNull(function);
|
||||||
|
this.resultEquivalence = checkNotNull(resultEquivalence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doEquivalent(F a, F b) {
|
||||||
|
return resultEquivalence.equivalent(function.apply(a), function.apply(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doHash(F a) {
|
||||||
|
return resultEquivalence.hash(function.apply(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof FunctionalEquivalence) {
|
||||||
|
FunctionalEquivalence<?, ?> that = (FunctionalEquivalence<?, ?>) obj;
|
||||||
|
return function.equals(that.function) && resultEquivalence.equals(that.resultEquivalence);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(function, resultEquivalence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return resultEquivalence + ".onResultOf(" + function + ")";
|
||||||
|
}
|
||||||
|
}
|
400
src/main/java/com/google/common/base/Functions.java
Normal file
400
src/main/java/com/google/common/base/Functions.java
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods pertaining to {@code com.google.common.base.Function} instances; see that
|
||||||
|
* class for information about migrating to {@code java.util.function}.
|
||||||
|
*
|
||||||
|
* <p>All methods return serializable functions as long as they're given serializable parameters.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/FunctionalExplained">the use of {@code Function}</a>.
|
||||||
|
*
|
||||||
|
* @author Mike Bostock
|
||||||
|
* @author Jared Levy
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class Functions {
|
||||||
|
private Functions() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function equivalent to the method reference {@code Object::toString}, for users not yet using
|
||||||
|
* Java 8. The function simply invokes {@code toString} on its argument and returns the result. It
|
||||||
|
* throws a {@link NullPointerException} on null input.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> The returned function may not be <i>consistent with equals</i> (as
|
||||||
|
* documented at {@link Function#apply}). For example, this function yields different results for
|
||||||
|
* the two equal instances {@code ImmutableSet.of(1, 2)} and {@code ImmutableSet.of(2, 1)}.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> as with all function types in this package, avoid depending on the specific
|
||||||
|
* {@code equals}, {@code hashCode} or {@code toString} behavior of the returned function. A
|
||||||
|
* future migration to {@code java.util.function} will not preserve this behavior.
|
||||||
|
*
|
||||||
|
* <p><b>For Java 8 users:</b> use the method reference {@code Object::toString} instead. In the
|
||||||
|
* future, when this class requires Java 8, this method will be deprecated. See {@link Function}
|
||||||
|
* for more important information about the Java 8 transition.
|
||||||
|
*/
|
||||||
|
public static Function<Object, String> toStringFunction() {
|
||||||
|
return ToStringFunction.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enum singleton pattern
|
||||||
|
private enum ToStringFunction implements Function<Object, String> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(Object o) {
|
||||||
|
checkNotNull(o); // eager for GWT.
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Functions.toStringFunction()";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the identity function. */
|
||||||
|
// implementation is "fully variant"; E has become a "pass-through" type
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <E> Function<E, E> identity() {
|
||||||
|
return (Function<E, E>) IdentityFunction.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enum singleton pattern
|
||||||
|
private enum IdentityFunction implements Function<Object, Object> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(Object o) {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Functions.identity()";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function which performs a map lookup. The returned function throws an {@link
|
||||||
|
* IllegalArgumentException} if given a key that does not exist in the map. See also {@link
|
||||||
|
* #forMap(Map, Object)}, which returns a default value in this case.
|
||||||
|
*
|
||||||
|
* <p>Note: if {@code map} is a {@link com.google.common.collect.BiMap BiMap} (or can be one), you
|
||||||
|
* can use {@link com.google.common.collect.Maps#asConverter Maps.asConverter} instead to get a
|
||||||
|
* function that also supports reverse conversion.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> if you are okay with {@code null} being returned for an unrecognized
|
||||||
|
* key (instead of an exception being thrown), you can use the method reference {@code map::get}
|
||||||
|
* instead.
|
||||||
|
*/
|
||||||
|
public static <K, V> Function<K, V> forMap(Map<K, V> map) {
|
||||||
|
return new FunctionForMapNoDefault<>(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function which performs a map lookup with a default value. The function created by
|
||||||
|
* this method returns {@code defaultValue} for all inputs that do not belong to the map's key
|
||||||
|
* set. See also {@link #forMap(Map)}, which throws an exception in this case.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> you can just write the lambda expression {@code k ->
|
||||||
|
* map.getOrDefault(k, defaultValue)} instead.
|
||||||
|
*
|
||||||
|
* @param map source map that determines the function behavior
|
||||||
|
* @param defaultValue the value to return for inputs that aren't map keys
|
||||||
|
* @return function that returns {@code map.get(a)} when {@code a} is a key, or {@code
|
||||||
|
* defaultValue} otherwise
|
||||||
|
*/
|
||||||
|
public static <K, V> Function<K, V> forMap(Map<K, ? extends V> map, V defaultValue) {
|
||||||
|
return new ForMapWithDefault<>(map, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FunctionForMapNoDefault<K, V> implements Function<K, V>, Serializable {
|
||||||
|
final Map<K, V> map;
|
||||||
|
|
||||||
|
FunctionForMapNoDefault(Map<K, V> map) {
|
||||||
|
this.map = checkNotNull(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V apply(K key) {
|
||||||
|
V result = map.get(key);
|
||||||
|
checkArgument(result != null || map.containsKey(key), "Key '%s' not present in map", key);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof FunctionForMapNoDefault) {
|
||||||
|
FunctionForMapNoDefault<?, ?> that = (FunctionForMapNoDefault<?, ?>) o;
|
||||||
|
return map.equals(that.map);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return map.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Functions.forMap(" + map + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ForMapWithDefault<K, V> implements Function<K, V>, Serializable {
|
||||||
|
final Map<K, ? extends V> map;
|
||||||
|
final V defaultValue;
|
||||||
|
|
||||||
|
ForMapWithDefault(Map<K, ? extends V> map, V defaultValue) {
|
||||||
|
this.map = checkNotNull(map);
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V apply(K key) {
|
||||||
|
V result = map.get(key);
|
||||||
|
return (result != null || map.containsKey(key)) ? result : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof ForMapWithDefault) {
|
||||||
|
ForMapWithDefault<?, ?> that = (ForMapWithDefault<?, ?>) o;
|
||||||
|
return map.equals(that.map) && Objects.equal(defaultValue, that.defaultValue);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(map, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// TODO(cpovirk): maybe remove "defaultValue=" to make this look like the method call does
|
||||||
|
return "Functions.forMap(" + map + ", defaultValue=" + defaultValue + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the composition of two functions. For {@code f: A->B} and {@code g: B->C}, composition
|
||||||
|
* is defined as the function h such that {@code h(a) == g(f(a))} for each {@code a}.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> use {@code g.compose(f)} or (probably clearer) {@code f.andThen(g)}
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* @param g the second function to apply
|
||||||
|
* @param f the first function to apply
|
||||||
|
* @return the composition of {@code f} and {@code g}
|
||||||
|
* @see <a href="//en.wikipedia.org/wiki/Function_composition">function composition</a>
|
||||||
|
*/
|
||||||
|
public static <A, B, C> Function<A, C> compose(Function<B, C> g, Function<A, ? extends B> f) {
|
||||||
|
return new FunctionComposition<>(g, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FunctionComposition<A, B, C> implements Function<A, C>, Serializable {
|
||||||
|
private final Function<B, C> g;
|
||||||
|
private final Function<A, ? extends B> f;
|
||||||
|
|
||||||
|
public FunctionComposition(Function<B, C> g, Function<A, ? extends B> f) {
|
||||||
|
this.g = checkNotNull(g);
|
||||||
|
this.f = checkNotNull(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public C apply(A a) {
|
||||||
|
return g.apply(f.apply(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof FunctionComposition) {
|
||||||
|
FunctionComposition<?, ?, ?> that = (FunctionComposition<?, ?, ?>) obj;
|
||||||
|
return f.equals(that.f) && g.equals(that.g);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return f.hashCode() ^ g.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// TODO(cpovirk): maybe make this look like the method call does ("Functions.compose(...)")
|
||||||
|
return g + "(" + f + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a function that returns the same boolean output as the given predicate for all inputs.
|
||||||
|
*
|
||||||
|
* <p>The returned function is <i>consistent with equals</i> (as documented at {@link
|
||||||
|
* Function#apply}) if and only if {@code predicate} is itself consistent with equals.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> use the method reference {@code predicate::test} instead.
|
||||||
|
*/
|
||||||
|
public static <T> Function<T, Boolean> forPredicate(Predicate<T> predicate) {
|
||||||
|
return new PredicateFunction<T>(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Functions#forPredicate */
|
||||||
|
private static class PredicateFunction<T> implements Function<T, Boolean>, Serializable {
|
||||||
|
private final Predicate<T> predicate;
|
||||||
|
|
||||||
|
private PredicateFunction(Predicate<T> predicate) {
|
||||||
|
this.predicate = checkNotNull(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean apply(T t) {
|
||||||
|
return predicate.apply(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof PredicateFunction) {
|
||||||
|
PredicateFunction<?> that = (PredicateFunction<?>) obj;
|
||||||
|
return predicate.equals(that.predicate);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return predicate.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Functions.forPredicate(" + predicate + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that ignores its input and always returns {@code value}.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> use the lambda expression {@code o -> value} instead.
|
||||||
|
*
|
||||||
|
* @param value the constant value for the function to return
|
||||||
|
* @return a function that always returns {@code value}
|
||||||
|
*/
|
||||||
|
public static <E> Function<Object, E> constant(E value) {
|
||||||
|
return new ConstantFunction<E>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ConstantFunction<E> implements Function<Object, E>, Serializable {
|
||||||
|
private final E value;
|
||||||
|
|
||||||
|
public ConstantFunction(E value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E apply(Object from) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ConstantFunction) {
|
||||||
|
ConstantFunction<?> that = (ConstantFunction<?>) obj;
|
||||||
|
return Objects.equal(value, that.value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (value == null) ? 0 : value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Functions.constant(" + value + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that ignores its input and returns the result of {@code supplier.get()}.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> use the lambda expression {@code o -> supplier.get()} instead.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public static <T> Function<Object, T> forSupplier(Supplier<T> supplier) {
|
||||||
|
return new SupplierFunction<T>(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Functions#forSupplier */
|
||||||
|
private static class SupplierFunction<T> implements Function<Object, T>, Serializable {
|
||||||
|
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
|
||||||
|
private SupplierFunction(Supplier<T> supplier) {
|
||||||
|
this.supplier = checkNotNull(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply(Object input) {
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof SupplierFunction) {
|
||||||
|
SupplierFunction<?> that = (SupplierFunction<?>) obj;
|
||||||
|
return this.supplier.equals(that.supplier);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return supplier.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Functions.forSupplier(" + supplier + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
}
|
90
src/main/java/com/google/common/base/JdkPattern.java
Normal file
90
src/main/java/com/google/common/base/JdkPattern.java
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** A regex pattern implementation which is backed by the {@link Pattern}. */
|
||||||
|
@GwtIncompatible
|
||||||
|
final class JdkPattern extends CommonPattern implements Serializable {
|
||||||
|
private final Pattern pattern;
|
||||||
|
|
||||||
|
JdkPattern(Pattern pattern) {
|
||||||
|
this.pattern = Preconditions.checkNotNull(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonMatcher matcher(CharSequence t) {
|
||||||
|
return new JdkMatcher(pattern.matcher(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pattern() {
|
||||||
|
return pattern.pattern();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int flags() {
|
||||||
|
return pattern.flags();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return pattern.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class JdkMatcher extends CommonMatcher {
|
||||||
|
final Matcher matcher;
|
||||||
|
|
||||||
|
JdkMatcher(Matcher matcher) {
|
||||||
|
this.matcher = Preconditions.checkNotNull(matcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches() {
|
||||||
|
return matcher.matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean find() {
|
||||||
|
return matcher.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean find(int index) {
|
||||||
|
return matcher.find(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String replaceAll(String replacement) {
|
||||||
|
return matcher.replaceAll(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int end() {
|
||||||
|
return matcher.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int start() {
|
||||||
|
return matcher.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
462
src/main/java/com/google/common/base/Joiner.java
Normal file
462
src/main/java/com/google/common/base/Joiner.java
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a
|
||||||
|
* {@link Map}) with a separator. It either appends the results to an {@link Appendable} or returns
|
||||||
|
* them as a {@link String}. Example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Joiner joiner = Joiner.on("; ").skipNulls();
|
||||||
|
* . . .
|
||||||
|
* return joiner.join("Harry", null, "Ron", "Hermione");
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>This returns the string {@code "Harry; Ron; Hermione"}. Note that all input elements are
|
||||||
|
* converted to strings using {@link Object#toString()} before being appended.
|
||||||
|
*
|
||||||
|
* <p>If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified, the joining
|
||||||
|
* methods will throw {@link NullPointerException} if any given element is null.
|
||||||
|
*
|
||||||
|
* <p><b>Warning: joiner instances are always immutable</b>; a configuration method such as {@code
|
||||||
|
* useForNull} has no effect on the instance it is invoked on! You must store and use the new joiner
|
||||||
|
* instance returned by the method. This makes joiners thread-safe, and safe to store as {@code
|
||||||
|
* static final} constants.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* // Bad! Do not do this!
|
||||||
|
* Joiner joiner = Joiner.on(',');
|
||||||
|
* joiner.skipNulls(); // does nothing!
|
||||||
|
* return joiner.join("wrong", null, "wrong");
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/StringsExplained#joiner">{@code Joiner}</a>.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public class Joiner {
|
||||||
|
/** Returns a joiner which automatically places {@code separator} between consecutive elements. */
|
||||||
|
public static Joiner on(String separator) {
|
||||||
|
return new Joiner(separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a joiner which automatically places {@code separator} between consecutive elements. */
|
||||||
|
public static Joiner on(char separator) {
|
||||||
|
return new Joiner(String.valueOf(separator));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String separator;
|
||||||
|
|
||||||
|
private Joiner(String separator) {
|
||||||
|
this.separator = checkNotNull(separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Joiner(Joiner prototype) {
|
||||||
|
this.separator = prototype.separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each of {@code parts}, using the previously configured
|
||||||
|
* separator between each, to {@code appendable}.
|
||||||
|
*/
|
||||||
|
public <A extends Appendable> A appendTo(A appendable, Iterable<?> parts) throws IOException {
|
||||||
|
return appendTo(appendable, parts.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each of {@code parts}, using the previously configured
|
||||||
|
* separator between each, to {@code appendable}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
|
||||||
|
checkNotNull(appendable);
|
||||||
|
if (parts.hasNext()) {
|
||||||
|
appendable.append(toString(parts.next()));
|
||||||
|
while (parts.hasNext()) {
|
||||||
|
appendable.append(separator);
|
||||||
|
appendable.append(toString(parts.next()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appendable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each of {@code parts}, using the previously configured
|
||||||
|
* separator between each, to {@code appendable}.
|
||||||
|
*/
|
||||||
|
public final <A extends Appendable> A appendTo(A appendable, Object[] parts) throws IOException {
|
||||||
|
return appendTo(appendable, Arrays.asList(parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Appends to {@code appendable} the string representation of each of the remaining arguments. */
|
||||||
|
public final <A extends Appendable> A appendTo(
|
||||||
|
A appendable, Object first, Object second, Object... rest)
|
||||||
|
throws IOException {
|
||||||
|
return appendTo(appendable, iterable(first, second, rest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each of {@code parts}, using the previously configured
|
||||||
|
* separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
|
||||||
|
* Iterable)}, except that it does not throw {@link IOException}.
|
||||||
|
*/
|
||||||
|
public final StringBuilder appendTo(StringBuilder builder, Iterable<?> parts) {
|
||||||
|
return appendTo(builder, parts.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each of {@code parts}, using the previously configured
|
||||||
|
* separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
|
||||||
|
* Iterable)}, except that it does not throw {@link IOException}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
|
||||||
|
try {
|
||||||
|
appendTo((Appendable) builder, parts);
|
||||||
|
} catch (IOException impossible) {
|
||||||
|
throw new AssertionError(impossible);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each of {@code parts}, using the previously configured
|
||||||
|
* separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
|
||||||
|
* Iterable)}, except that it does not throw {@link IOException}.
|
||||||
|
*/
|
||||||
|
public final StringBuilder appendTo(StringBuilder builder, Object[] parts) {
|
||||||
|
return appendTo(builder, Arrays.asList(parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends to {@code builder} the string representation of each of the remaining arguments.
|
||||||
|
* Identical to {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does not
|
||||||
|
* throw {@link IOException}.
|
||||||
|
*/
|
||||||
|
public final StringBuilder appendTo(
|
||||||
|
StringBuilder builder, Object first, Object second, Object... rest) {
|
||||||
|
return appendTo(builder, iterable(first, second, rest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each of {@code parts}, using the
|
||||||
|
* previously configured separator between each.
|
||||||
|
*/
|
||||||
|
public final String join(Iterable<?> parts) {
|
||||||
|
return join(parts.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each of {@code parts}, using the
|
||||||
|
* previously configured separator between each.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public final String join(Iterator<?> parts) {
|
||||||
|
return appendTo(new StringBuilder(), parts).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each of {@code parts}, using the
|
||||||
|
* previously configured separator between each.
|
||||||
|
*/
|
||||||
|
public final String join(Object[] parts) {
|
||||||
|
return join(Arrays.asList(parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each argument, using the previously
|
||||||
|
* configured separator between each.
|
||||||
|
*/
|
||||||
|
public final String join(Object first, Object second, Object... rest) {
|
||||||
|
return join(iterable(first, second, rest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a joiner with the same behavior as this one, except automatically substituting {@code
|
||||||
|
* nullText} for any provided null elements.
|
||||||
|
*/
|
||||||
|
public Joiner useForNull(final String nullText) {
|
||||||
|
checkNotNull(nullText);
|
||||||
|
return new Joiner(this) {
|
||||||
|
@Override
|
||||||
|
CharSequence toString(Object part) {
|
||||||
|
return (part == null) ? nullText : Joiner.this.toString(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Joiner useForNull(String nullText) {
|
||||||
|
throw new UnsupportedOperationException("already specified useForNull");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Joiner skipNulls() {
|
||||||
|
throw new UnsupportedOperationException("already specified useForNull");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a joiner with the same behavior as this joiner, except automatically skipping over any
|
||||||
|
* provided null elements.
|
||||||
|
*/
|
||||||
|
public Joiner skipNulls() {
|
||||||
|
return new Joiner(this) {
|
||||||
|
@Override
|
||||||
|
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
|
||||||
|
checkNotNull(appendable, "appendable");
|
||||||
|
checkNotNull(parts, "parts");
|
||||||
|
while (parts.hasNext()) {
|
||||||
|
Object part = parts.next();
|
||||||
|
if (part != null) {
|
||||||
|
appendable.append(Joiner.this.toString(part));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (parts.hasNext()) {
|
||||||
|
Object part = parts.next();
|
||||||
|
if (part != null) {
|
||||||
|
appendable.append(separator);
|
||||||
|
appendable.append(Joiner.this.toString(part));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appendable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Joiner useForNull(String nullText) {
|
||||||
|
throw new UnsupportedOperationException("already specified skipNulls");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapJoiner withKeyValueSeparator(String kvs) {
|
||||||
|
throw new UnsupportedOperationException("can't use .skipNulls() with maps");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
|
||||||
|
* this {@code Joiner} otherwise.
|
||||||
|
*
|
||||||
|
* @since 20.0
|
||||||
|
*/
|
||||||
|
public MapJoiner withKeyValueSeparator(char keyValueSeparator) {
|
||||||
|
return withKeyValueSeparator(String.valueOf(keyValueSeparator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
|
||||||
|
* this {@code Joiner} otherwise.
|
||||||
|
*/
|
||||||
|
public MapJoiner withKeyValueSeparator(String keyValueSeparator) {
|
||||||
|
return new MapJoiner(this, keyValueSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that joins map entries in the same manner as {@code Joiner} joins iterables and
|
||||||
|
* arrays. Like {@code Joiner}, it is thread-safe and immutable.
|
||||||
|
*
|
||||||
|
* <p>In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code
|
||||||
|
* Multimap} entries in two distinct modes:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>To output a separate entry for each key-value pair, pass {@code multimap.entries()} to a
|
||||||
|
* {@code MapJoiner} method that accepts entries as input, and receive output of the form
|
||||||
|
* {@code key1=A&key1=B&key2=C}.
|
||||||
|
* <li>To output a single entry for each key, pass {@code multimap.asMap()} to a {@code
|
||||||
|
* MapJoiner} method that accepts a map as input, and receive output of the form {@code
|
||||||
|
* key1=[A, B]&key2=C}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static final class MapJoiner {
|
||||||
|
private final Joiner joiner;
|
||||||
|
private final String keyValueSeparator;
|
||||||
|
|
||||||
|
private MapJoiner(Joiner joiner, String keyValueSeparator) {
|
||||||
|
this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull
|
||||||
|
this.keyValueSeparator = checkNotNull(keyValueSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each entry of {@code map}, using the previously
|
||||||
|
* configured separator and key-value separator, to {@code appendable}.
|
||||||
|
*/
|
||||||
|
public <A extends Appendable> A appendTo(A appendable, Map<?, ?> map) throws IOException {
|
||||||
|
return appendTo(appendable, map.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each entry of {@code map}, using the previously
|
||||||
|
* configured separator and key-value separator, to {@code builder}. Identical to {@link
|
||||||
|
* #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}.
|
||||||
|
*/
|
||||||
|
public StringBuilder appendTo(StringBuilder builder, Map<?, ?> map) {
|
||||||
|
return appendTo(builder, map.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each entry in {@code entries}, using the previously
|
||||||
|
* configured separator and key-value separator, to {@code appendable}.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public <A extends Appendable> A appendTo(A appendable, Iterable<? extends Entry<?, ?>> entries)
|
||||||
|
throws IOException {
|
||||||
|
return appendTo(appendable, entries.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each entry in {@code entries}, using the previously
|
||||||
|
* configured separator and key-value separator, to {@code appendable}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public <A extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> parts)
|
||||||
|
throws IOException {
|
||||||
|
checkNotNull(appendable);
|
||||||
|
if (parts.hasNext()) {
|
||||||
|
Entry<?, ?> entry = parts.next();
|
||||||
|
appendable.append(joiner.toString(entry.getKey()));
|
||||||
|
appendable.append(keyValueSeparator);
|
||||||
|
appendable.append(joiner.toString(entry.getValue()));
|
||||||
|
while (parts.hasNext()) {
|
||||||
|
appendable.append(joiner.separator);
|
||||||
|
Entry<?, ?> e = parts.next();
|
||||||
|
appendable.append(joiner.toString(e.getKey()));
|
||||||
|
appendable.append(keyValueSeparator);
|
||||||
|
appendable.append(joiner.toString(e.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appendable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each entry in {@code entries}, using the previously
|
||||||
|
* configured separator and key-value separator, to {@code builder}. Identical to {@link
|
||||||
|
* #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public StringBuilder appendTo(StringBuilder builder, Iterable<? extends Entry<?, ?>> entries) {
|
||||||
|
return appendTo(builder, entries.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the string representation of each entry in {@code entries}, using the previously
|
||||||
|
* configured separator and key-value separator, to {@code builder}. Identical to {@link
|
||||||
|
* #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public StringBuilder appendTo(StringBuilder builder, Iterator<? extends Entry<?, ?>> entries) {
|
||||||
|
try {
|
||||||
|
appendTo((Appendable) builder, entries);
|
||||||
|
} catch (IOException impossible) {
|
||||||
|
throw new AssertionError(impossible);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each entry of {@code map}, using the
|
||||||
|
* previously configured separator and key-value separator.
|
||||||
|
*/
|
||||||
|
public String join(Map<?, ?> map) {
|
||||||
|
return join(map.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each entry in {@code entries}, using
|
||||||
|
* the previously configured separator and key-value separator.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public String join(Iterable<? extends Entry<?, ?>> entries) {
|
||||||
|
return join(entries.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the string representation of each entry in {@code entries}, using
|
||||||
|
* the previously configured separator and key-value separator.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public String join(Iterator<? extends Entry<?, ?>> entries) {
|
||||||
|
return appendTo(new StringBuilder(), entries).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map joiner with the same behavior as this one, except automatically substituting
|
||||||
|
* {@code nullText} for any provided null keys or values.
|
||||||
|
*/
|
||||||
|
public MapJoiner useForNull(String nullText) {
|
||||||
|
return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence toString(Object part) {
|
||||||
|
checkNotNull(part); // checkNotNull for GWT (do not optimize).
|
||||||
|
return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Iterable<Object> iterable(
|
||||||
|
final Object first, final Object second, final Object[] rest) {
|
||||||
|
checkNotNull(rest);
|
||||||
|
return new AbstractList<Object>() {
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return rest.length + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return first;
|
||||||
|
case 1:
|
||||||
|
return second;
|
||||||
|
default:
|
||||||
|
return rest[index - 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
373
src/main/java/com/google/common/base/MoreObjects.java
Normal file
373
src/main/java/com/google/common/base/MoreObjects.java
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions that operate on any {@code Object}, and are not already provided in {@link
|
||||||
|
* java.util.Objects}.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained">writing {@code Object}
|
||||||
|
* methods with {@code MoreObjects}</a>.
|
||||||
|
*
|
||||||
|
* @author Laurence Gonsalves
|
||||||
|
* @since 18.0 (since 2.0 as {@code Objects})
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class MoreObjects {
|
||||||
|
/**
|
||||||
|
* Returns the first of two given parameters that is not {@code null}, if either is, or otherwise
|
||||||
|
* throws a {@link NullPointerException}.
|
||||||
|
*
|
||||||
|
* <p>To find the first non-null element in an iterable, use {@code Iterables.find(iterable,
|
||||||
|
* Predicates.notNull())}. For varargs, use {@code Iterables.find(Arrays.asList(a, b, c, ...),
|
||||||
|
* Predicates.notNull())}, static importing as necessary.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> if {@code first} is represented as an {@link Optional}, this can be
|
||||||
|
* accomplished with {@link Optional#or(Object) first.or(second)}. That approach also allows for
|
||||||
|
* lazy evaluation of the fallback instance, using {@link Optional#or(Supplier)
|
||||||
|
* first.or(supplier)}.
|
||||||
|
*
|
||||||
|
* <p><b>Java 9 users:</b> use {@code java.util.Objects.requireNonNullElse(first, second)}
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* @return {@code first} if it is non-null; otherwise {@code second} if it is non-null
|
||||||
|
* @throws NullPointerException if both {@code first} and {@code second} are null
|
||||||
|
* @since 18.0 (since 3.0 as {@code Objects.firstNonNull()}).
|
||||||
|
*/
|
||||||
|
public static <T> T firstNonNull(T first, T second) {
|
||||||
|
if (first != null) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
if (second != null) {
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
throw new NullPointerException("Both parameters are null");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ToStringHelper}.
|
||||||
|
*
|
||||||
|
* <p>This is helpful for implementing {@link Object#toString()}. Specification by example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* // Returns "ClassName{}"
|
||||||
|
* MoreObjects.toStringHelper(this)
|
||||||
|
* .toString();
|
||||||
|
*
|
||||||
|
* // Returns "ClassName{x=1}"
|
||||||
|
* MoreObjects.toStringHelper(this)
|
||||||
|
* .add("x", 1)
|
||||||
|
* .toString();
|
||||||
|
*
|
||||||
|
* // Returns "MyObject{x=1}"
|
||||||
|
* MoreObjects.toStringHelper("MyObject")
|
||||||
|
* .add("x", 1)
|
||||||
|
* .toString();
|
||||||
|
*
|
||||||
|
* // Returns "ClassName{x=1, y=foo}"
|
||||||
|
* MoreObjects.toStringHelper(this)
|
||||||
|
* .add("x", 1)
|
||||||
|
* .add("y", "foo")
|
||||||
|
* .toString();
|
||||||
|
*
|
||||||
|
* // Returns "ClassName{x=1}"
|
||||||
|
* MoreObjects.toStringHelper(this)
|
||||||
|
* .omitNullValues()
|
||||||
|
* .add("x", 1)
|
||||||
|
* .add("y", null)
|
||||||
|
* .toString();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Note that in GWT, class names are often obfuscated.
|
||||||
|
*
|
||||||
|
* @param self the object to generate the string for (typically {@code this}), used only for its
|
||||||
|
* class name
|
||||||
|
* @since 18.0 (since 2.0 as {@code Objects.toStringHelper()}).
|
||||||
|
*/
|
||||||
|
public static ToStringHelper toStringHelper(Object self) {
|
||||||
|
return new ToStringHelper(self.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ToStringHelper} in the same manner as {@link
|
||||||
|
* #toStringHelper(Object)}, but using the simple name of {@code clazz} instead of using an
|
||||||
|
* instance's {@link Object#getClass()}.
|
||||||
|
*
|
||||||
|
* <p>Note that in GWT, class names are often obfuscated.
|
||||||
|
*
|
||||||
|
* @param clazz the {@link Class} of the instance
|
||||||
|
* @since 18.0 (since 7.0 as {@code Objects.toStringHelper()}).
|
||||||
|
*/
|
||||||
|
public static ToStringHelper toStringHelper(Class<?> clazz) {
|
||||||
|
return new ToStringHelper(clazz.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ToStringHelper} in the same manner as {@link
|
||||||
|
* #toStringHelper(Object)}, but using {@code className} instead of using an instance's {@link
|
||||||
|
* Object#getClass()}.
|
||||||
|
*
|
||||||
|
* @param className the name of the instance type
|
||||||
|
* @since 18.0 (since 7.0 as {@code Objects.toStringHelper()}).
|
||||||
|
*/
|
||||||
|
public static ToStringHelper toStringHelper(String className) {
|
||||||
|
return new ToStringHelper(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support class for {@link MoreObjects#toStringHelper}.
|
||||||
|
*
|
||||||
|
* @author Jason Lee
|
||||||
|
* @since 18.0 (since 2.0 as {@code Objects.ToStringHelper}).
|
||||||
|
*/
|
||||||
|
public static final class ToStringHelper {
|
||||||
|
private final String className;
|
||||||
|
private final ValueHolder holderHead = new ValueHolder();
|
||||||
|
private ValueHolder holderTail = holderHead;
|
||||||
|
private boolean omitNullValues = false;
|
||||||
|
|
||||||
|
/** Use {@link MoreObjects#toStringHelper(Object)} to create an instance. */
|
||||||
|
private ToStringHelper(String className) {
|
||||||
|
this.className = checkNotNull(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the {@link ToStringHelper} so {@link #toString()} will ignore properties with null
|
||||||
|
* value. The order of calling this method, relative to the {@code add()}/{@code addValue()}
|
||||||
|
* methods, is not significant.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 12.0 as {@code Objects.ToStringHelper.omitNullValues()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper omitNullValues() {
|
||||||
|
omitNullValues = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format. If {@code value}
|
||||||
|
* is {@code null}, the string {@code "null"} is used, unless {@link #omitNullValues()} is
|
||||||
|
* called, in which case this name/value pair will not be added.
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, Object value) {
|
||||||
|
return addHolder(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, boolean value) {
|
||||||
|
return addHolder(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, char value) {
|
||||||
|
return addHolder(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, double value) {
|
||||||
|
return addHolder(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, float value) {
|
||||||
|
return addHolder(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, int value) {
|
||||||
|
return addHolder(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a name/value pair to the formatted output in {@code name=value} format.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper add(String name, long value) {
|
||||||
|
return addHolder(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, Object)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(Object value) {
|
||||||
|
return addHolder(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, boolean)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(boolean value) {
|
||||||
|
return addHolder(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, char)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(char value) {
|
||||||
|
return addHolder(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, double)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(double value) {
|
||||||
|
return addHolder(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, float)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(float value) {
|
||||||
|
return addHolder(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, int)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(int value) {
|
||||||
|
return addHolder(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unnamed value to the formatted output.
|
||||||
|
*
|
||||||
|
* <p>It is strongly encouraged to use {@link #add(String, long)} instead and give value a
|
||||||
|
* readable name.
|
||||||
|
*
|
||||||
|
* @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}).
|
||||||
|
*/
|
||||||
|
public ToStringHelper addValue(long value) {
|
||||||
|
return addHolder(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string in the format specified by {@link MoreObjects#toStringHelper(Object)}.
|
||||||
|
*
|
||||||
|
* <p>After calling this method, you can keep adding more properties to later call toString()
|
||||||
|
* again and get a more complete representation of the same object; but properties cannot be
|
||||||
|
* removed, so this only allows limited reuse of the helper instance. The helper allows
|
||||||
|
* duplication of properties (multiple name/value pairs with the same name can be added).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// create a copy to keep it consistent in case value changes
|
||||||
|
boolean omitNullValuesSnapshot = omitNullValues;
|
||||||
|
String nextSeparator = "";
|
||||||
|
StringBuilder builder = new StringBuilder(32).append(className).append('{');
|
||||||
|
for (ValueHolder valueHolder = holderHead.next;
|
||||||
|
valueHolder != null;
|
||||||
|
valueHolder = valueHolder.next) {
|
||||||
|
Object value = valueHolder.value;
|
||||||
|
if (!omitNullValuesSnapshot || value != null) {
|
||||||
|
builder.append(nextSeparator);
|
||||||
|
nextSeparator = ", ";
|
||||||
|
|
||||||
|
if (valueHolder.name != null) {
|
||||||
|
builder.append(valueHolder.name).append('=');
|
||||||
|
}
|
||||||
|
if (value != null && value.getClass().isArray()) {
|
||||||
|
Object[] objectArray = {value};
|
||||||
|
String arrayString = Arrays.deepToString(objectArray);
|
||||||
|
builder.append(arrayString, 1, arrayString.length() - 1);
|
||||||
|
} else {
|
||||||
|
builder.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.append('}').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueHolder addHolder() {
|
||||||
|
ValueHolder valueHolder = new ValueHolder();
|
||||||
|
holderTail = holderTail.next = valueHolder;
|
||||||
|
return valueHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ToStringHelper addHolder(Object value) {
|
||||||
|
ValueHolder valueHolder = addHolder();
|
||||||
|
valueHolder.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ToStringHelper addHolder(String name, Object value) {
|
||||||
|
ValueHolder valueHolder = addHolder();
|
||||||
|
valueHolder.value = value;
|
||||||
|
valueHolder.name = checkNotNull(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ValueHolder {
|
||||||
|
String name;
|
||||||
|
Object value;
|
||||||
|
ValueHolder next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MoreObjects() {}
|
||||||
|
}
|
78
src/main/java/com/google/common/base/Objects.java
Normal file
78
src/main/java/com/google/common/base/Objects.java
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions that can operate on any {@code Object}.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained">writing {@code Object}
|
||||||
|
* methods with {@code Objects}</a>.
|
||||||
|
*
|
||||||
|
* @author Laurence Gonsalves
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class Objects extends ExtraObjectsMethodsForWeb {
|
||||||
|
private Objects() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether two possibly-null objects are equal. Returns:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code true} if {@code a} and {@code b} are both null.
|
||||||
|
* <li>{@code true} if {@code a} and {@code b} are both non-null and they are equal according to
|
||||||
|
* {@link Object#equals(Object)}.
|
||||||
|
* <li>{@code false} in all other situations.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>This assumes that any non-null objects passed to this function conform to the {@code
|
||||||
|
* equals()} contract.
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> This method should be treated as deprecated; use {@link
|
||||||
|
* java.util.Objects#equals} instead.
|
||||||
|
*/
|
||||||
|
public static boolean equal(Object a, Object b) {
|
||||||
|
return a == b || (a != null && a.equals(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a hash code for multiple values. The hash code is generated by calling {@link
|
||||||
|
* Arrays#hashCode(Object[])}. Note that array arguments to this method, with the exception of a
|
||||||
|
* single Object array, do not get any special handling; their hash codes are based on identity
|
||||||
|
* and not contents.
|
||||||
|
*
|
||||||
|
* <p>This is useful for implementing {@link Object#hashCode()}. For example, in an object that
|
||||||
|
* has three properties, {@code x}, {@code y}, and {@code z}, one could write:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* public int hashCode() {
|
||||||
|
* return Objects.hashCode(getX(), getY(), getZ());
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> When a single object is supplied, the returned hash code does not equal the
|
||||||
|
* hash code of that object.
|
||||||
|
*
|
||||||
|
* <p><b>Note for Java 7 and later:</b> This method should be treated as deprecated; use {@link
|
||||||
|
* java.util.Objects#hash} instead.
|
||||||
|
*/
|
||||||
|
public static int hashCode(Object ... objects) {
|
||||||
|
return Arrays.hashCode(objects);
|
||||||
|
}
|
||||||
|
}
|
356
src/main/java/com/google/common/base/Optional.java
Normal file
356
src/main/java/com/google/common/base/Optional.java
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable object that may contain a non-null reference to another object. Each instance of
|
||||||
|
* this type either contains a non-null reference, or contains nothing (in which case we say that
|
||||||
|
* the reference is "absent"); it is never said to "contain {@code null}".
|
||||||
|
*
|
||||||
|
* <p>A non-null {@code Optional<T>} reference can be used as a replacement for a nullable {@code T}
|
||||||
|
* reference. It allows you to represent "a {@code T} that must be present" and a "a {@code T} that
|
||||||
|
* might be absent" as two distinct types in your program, which can aid clarity.
|
||||||
|
*
|
||||||
|
* <p>Some uses of this class include
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>As a method return type, as an alternative to returning {@code null} to indicate that no
|
||||||
|
* value was available
|
||||||
|
* <li>To distinguish between "unknown" (for example, not present in a map) and "known to have no
|
||||||
|
* value" (present in the map, with value {@code Optional.absent()})
|
||||||
|
* <li>To wrap nullable references for storage in a collection that does not support {@code null}
|
||||||
|
* (though there are <a
|
||||||
|
* href="https://github.com/google/guava/wiki/LivingWithNullHostileCollections">several other
|
||||||
|
* approaches to this</a> that should be considered first)
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A common alternative to using this class is to find or create a suitable <a
|
||||||
|
* href="http://en.wikipedia.org/wiki/Null_Object_pattern">null object</a> for the type in question.
|
||||||
|
*
|
||||||
|
* <p>This class is not intended as a direct analogue of any existing "option" or "maybe" construct
|
||||||
|
* from other programming environments, though it may bear some similarities.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional} (JDK 8 and higher):</b> A new {@code Optional}
|
||||||
|
* class was added for Java 8. The two classes are extremely similar, but incompatible (they cannot
|
||||||
|
* share a common supertype). <i>All</i> known differences are listed either here or with the
|
||||||
|
* relevant methods below.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>This class is serializable; {@code java.util.Optional} is not.
|
||||||
|
* <li>{@code java.util.Optional} has the additional methods {@code ifPresent}, {@code filter},
|
||||||
|
* {@code flatMap}, and {@code orElseThrow}.
|
||||||
|
* <li>{@code java.util} offers the primitive-specialized versions {@code OptionalInt}, {@code
|
||||||
|
* OptionalLong} and {@code OptionalDouble}, the use of which is recommended; Guava does not
|
||||||
|
* have these.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>There are no plans to deprecate this class in the foreseeable future.</b> However, we do
|
||||||
|
* gently recommend that you prefer the new, standard Java class whenever possible.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained#optional">using {@code
|
||||||
|
* Optional}</a>.
|
||||||
|
*
|
||||||
|
* @param <T> the type of instance that can be contained. {@code Optional} is naturally covariant on
|
||||||
|
* this type, so it is safe to cast an {@code Optional<T>} to {@code Optional<S>} for any
|
||||||
|
* supertype {@code S} of {@code T}.
|
||||||
|
* @author Kurt Alfred Kluever
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
public abstract class Optional<T> implements Serializable {
|
||||||
|
/**
|
||||||
|
* Returns an {@code Optional} instance with no contained reference.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method is equivalent to Java 8's
|
||||||
|
* {@code Optional.empty}.
|
||||||
|
*/
|
||||||
|
public static <T> Optional<T> absent() {
|
||||||
|
return Absent.withType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@code Optional} instance containing the given non-null reference. To have {@code
|
||||||
|
* null} treated as {@link #absent}, use {@link #fromNullable} instead.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> no differences.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if {@code reference} is null
|
||||||
|
*/
|
||||||
|
public static <T> Optional<T> of(T reference) {
|
||||||
|
return new Present<T>(checkNotNull(reference));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@code nullableReference} is non-null, returns an {@code Optional} instance containing that
|
||||||
|
* reference; otherwise returns {@link Optional#absent}.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method is equivalent to Java 8's
|
||||||
|
* {@code Optional.ofNullable}.
|
||||||
|
*/
|
||||||
|
public static <T> Optional<T> fromNullable(T nullableReference) {
|
||||||
|
return (nullableReference == null) ? Optional.<T>absent() : new Present<T>(nullableReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the equivalent {@code com.google.common.base.Optional} value to the given {@code
|
||||||
|
* java.util.Optional}, or {@code null} if the argument is null.
|
||||||
|
*
|
||||||
|
* @since 21.0
|
||||||
|
*/
|
||||||
|
public static <T> Optional<T> fromJavaUtil(
|
||||||
|
java.util.Optional<T> javaUtilOptional) {
|
||||||
|
return (javaUtilOptional == null) ? null : fromNullable(javaUtilOptional.orElse(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the equivalent {@code java.util.Optional} value to the given {@code
|
||||||
|
* com.google.common.base.Optional}, or {@code null} if the argument is null.
|
||||||
|
*
|
||||||
|
* <p>If {@code googleOptional} is known to be non-null, use {@code googleOptional.toJavaUtil()}
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* <p>Unfortunately, the method reference {@code Optional::toJavaUtil} will not work, because it
|
||||||
|
* could refer to either the static or instance version of this method. Write out the lambda
|
||||||
|
* expression {@code o -> Optional.toJavaUtil(o)} instead.
|
||||||
|
*
|
||||||
|
* @since 21.0
|
||||||
|
*/
|
||||||
|
public static <T> java.util.Optional<T> toJavaUtil(
|
||||||
|
Optional<T> googleOptional) {
|
||||||
|
return googleOptional == null ? null : googleOptional.toJavaUtil();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the equivalent {@code java.util.Optional} value to this optional.
|
||||||
|
*
|
||||||
|
* <p>Unfortunately, the method reference {@code Optional::toJavaUtil} will not work, because it
|
||||||
|
* could refer to either the static or instance version of this method. Write out the lambda
|
||||||
|
* expression {@code o -> o.toJavaUtil()} instead.
|
||||||
|
*
|
||||||
|
* @since 21.0
|
||||||
|
*/
|
||||||
|
public java.util.Optional<T> toJavaUtil() {
|
||||||
|
return java.util.Optional.ofNullable(orNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if this holder contains a (non-null) instance.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> no differences.
|
||||||
|
*/
|
||||||
|
public abstract boolean isPresent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contained instance, which must be present. If the instance might be absent, use
|
||||||
|
* {@link #or(Object)} or {@link #orNull} instead.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> when the value is absent, this method
|
||||||
|
* throws {@link IllegalStateException}, whereas the Java 8 counterpart throws {@link
|
||||||
|
* java.util.NoSuchElementException NoSuchElementException}.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the instance is absent ({@link #isPresent} returns {@code
|
||||||
|
* false}); depending on this <i>specific</i> exception type (over the more general {@link
|
||||||
|
* RuntimeException}) is discouraged
|
||||||
|
*/
|
||||||
|
public abstract T get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contained instance if it is present; {@code defaultValue} otherwise. If no default
|
||||||
|
* value should be required because the instance is known to be present, use {@link #get()}
|
||||||
|
* instead. For a default value of {@code null}, use {@link #orNull}.
|
||||||
|
*
|
||||||
|
* <p>Note about generics: The signature {@code public T or(T defaultValue)} is overly
|
||||||
|
* restrictive. However, the ideal signature, {@code public <S super T> S or(S)}, is not legal
|
||||||
|
* Java. As a result, some sensible operations involving subtypes are compile errors:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Optional<Integer> optionalInt = getSomeOptionalInt();
|
||||||
|
* Number value = optionalInt.or(0.5); // error
|
||||||
|
*
|
||||||
|
* FluentIterable<? extends Number> numbers = getSomeNumbers();
|
||||||
|
* Optional<? extends Number> first = numbers.first();
|
||||||
|
* Number value = first.or(0.5); // error
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>As a workaround, it is always safe to cast an {@code Optional<? extends T>} to {@code
|
||||||
|
* Optional<T>}. Casting either of the above example {@code Optional} instances to {@code
|
||||||
|
* Optional<Number>} (where {@code Number} is the desired output type) solves the problem:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
|
||||||
|
* Number value = optionalInt.or(0.5); // fine
|
||||||
|
*
|
||||||
|
* FluentIterable<? extends Number> numbers = getSomeNumbers();
|
||||||
|
* Optional<Number> first = (Optional) numbers.first();
|
||||||
|
* Number value = first.or(0.5); // fine
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method is similar to Java 8's {@code
|
||||||
|
* Optional.orElse}, but will not accept {@code null} as a {@code defaultValue} ({@link #orNull}
|
||||||
|
* must be used instead). As a result, the value returned by this method is guaranteed non-null,
|
||||||
|
* which is not the case for the {@code java.util} equivalent.
|
||||||
|
*/
|
||||||
|
public abstract T or(T defaultValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this {@code Optional} if it has a value present; {@code secondChoice} otherwise.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method has no equivalent in Java 8's
|
||||||
|
* {@code Optional} class; write {@code thisOptional.isPresent() ? thisOptional : secondChoice}
|
||||||
|
* instead.
|
||||||
|
*/
|
||||||
|
public abstract Optional<T> or(Optional<? extends T> secondChoice);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contained instance if it is present; {@code supplier.get()} otherwise.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method is similar to Java 8's {@code
|
||||||
|
* Optional.orElseGet}, except when {@code supplier} returns {@code null}. In this case this
|
||||||
|
* method throws an exception, whereas the Java 8 method returns the {@code null} to the caller.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if this optional's value is absent and the supplier returns {@code
|
||||||
|
* null}
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public abstract T or(Supplier<? extends T> supplier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contained instance if it is present; {@code null} otherwise. If the instance is
|
||||||
|
* known to be present, use {@link #get()} instead.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method is equivalent to Java 8's
|
||||||
|
* {@code Optional.orElse(null)}.
|
||||||
|
*/
|
||||||
|
public abstract T orNull();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable singleton {@link Set} whose only element is the contained instance if it
|
||||||
|
* is present; an empty immutable {@link Set} otherwise.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method has no equivalent in Java 8's
|
||||||
|
* {@code Optional} class. However, this common usage:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* for (Foo foo : possibleFoo.asSet()) {
|
||||||
|
* doSomethingWith(foo);
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* ... can be replaced with:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* possibleFoo.ifPresent(foo -> doSomethingWith(foo));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p><b>Java 9 users:</b> some use cases can be written with calls to {@code optional.stream()}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public abstract Set<T> asSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the instance is present, it is transformed with the given {@link Function}; otherwise,
|
||||||
|
* {@link Optional#absent} is returned.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method is similar to Java 8's {@code
|
||||||
|
* Optional.map}, except when {@code function} returns {@code null}. In this case this method
|
||||||
|
* throws an exception, whereas the Java 8 method returns {@code Optional.absent()}.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if the function returns {@code null}
|
||||||
|
* @since 12.0
|
||||||
|
*/
|
||||||
|
public abstract <V> Optional<V> transform(Function<? super T, V> function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if {@code object} is an {@code Optional} instance, and either the
|
||||||
|
* contained references are {@linkplain Object#equals equal} to each other or both are absent.
|
||||||
|
* Note that {@code Optional} instances of differing parameterized types can be equal.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> no differences.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract boolean equals(Object object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code for this instance.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this class leaves the specific choice of
|
||||||
|
* hash code unspecified, unlike the Java 8 equivalent.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract int hashCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation for this instance.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this class leaves the specific string
|
||||||
|
* representation unspecified, unlike the Java 8 equivalent.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of each present instance from the supplied {@code optionals}, in order,
|
||||||
|
* skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are
|
||||||
|
* evaluated lazily.
|
||||||
|
*
|
||||||
|
* <p><b>Comparison to {@code java.util.Optional}:</b> this method has no equivalent in Java 8's
|
||||||
|
* {@code Optional} class; use {@code
|
||||||
|
* optionals.stream().filter(Optional::isPresent).map(Optional::get)} instead.
|
||||||
|
*
|
||||||
|
* <p><b>Java 9 users:</b> use {@code optionals.stream().flatMap(Optional::stream)} instead.
|
||||||
|
*
|
||||||
|
* @since 11.0 (generics widened in 13.0)
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public static <T> Iterable<T> presentInstances(
|
||||||
|
final Iterable<? extends Optional<? extends T>> optionals) {
|
||||||
|
checkNotNull(optionals);
|
||||||
|
return new Iterable<T>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return new AbstractIterator<T>() {
|
||||||
|
private final Iterator<? extends Optional<? extends T>> iterator =
|
||||||
|
checkNotNull(optionals.iterator());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T computeNext() {
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Optional<? extends T> optional = iterator.next();
|
||||||
|
if (optional.isPresent()) {
|
||||||
|
return optional.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endOfData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
final class PairwiseEquivalence<T> extends Equivalence<Iterable<T>> implements Serializable {
|
||||||
|
|
||||||
|
final Equivalence<? super T> elementEquivalence;
|
||||||
|
|
||||||
|
PairwiseEquivalence(Equivalence<? super T> elementEquivalence) {
|
||||||
|
this.elementEquivalence = Preconditions.checkNotNull(elementEquivalence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doEquivalent(Iterable<T> iterableA, Iterable<T> iterableB) {
|
||||||
|
Iterator<T> iteratorA = iterableA.iterator();
|
||||||
|
Iterator<T> iteratorB = iterableB.iterator();
|
||||||
|
|
||||||
|
while (iteratorA.hasNext() && iteratorB.hasNext()) {
|
||||||
|
if (!elementEquivalence.equivalent(iteratorA.next(), iteratorB.next())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !iteratorA.hasNext() && !iteratorB.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doHash(Iterable<T> iterable) {
|
||||||
|
int hash = 78721;
|
||||||
|
for (T element : iterable) {
|
||||||
|
hash = hash * 24943 + elementEquivalence.hash(element);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof PairwiseEquivalence) {
|
||||||
|
PairwiseEquivalence<?> that = (PairwiseEquivalence<?>) object;
|
||||||
|
return this.elementEquivalence.equals(that.elementEquivalence);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return elementEquivalence.hashCode() ^ 0x46a3eb07;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return elementEquivalence + ".pairwise()";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1;
|
||||||
|
}
|
38
src/main/java/com/google/common/base/PatternCompiler.java
Normal file
38
src/main/java/com/google/common/base/PatternCompiler.java
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pluggable interface for compiling a regex pattern. By default this package uses the {@code
|
||||||
|
* java.util.regex} library, but an alternate implementation can be supplied using the {@link
|
||||||
|
* java.util.ServiceLoader} mechanism.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
interface PatternCompiler {
|
||||||
|
/**
|
||||||
|
* Compiles the given pattern.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the pattern is invalid
|
||||||
|
*/
|
||||||
|
CommonPattern compile(String pattern);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the regex implementation behaves like Perl -- notably, by supporting
|
||||||
|
* possessive quantifiers but also being susceptible to catastrophic backtracking.
|
||||||
|
*/
|
||||||
|
boolean isPcreLike();
|
||||||
|
}
|
121
src/main/java/com/google/common/base/Platform.java
Normal file
121
src/main/java/com/google/common/base/Platform.java
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.lenientFormat;
|
||||||
|
import static java.lang.Boolean.parseBoolean;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ServiceConfigurationError;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods factored out so that they can be emulated differently in GWT.
|
||||||
|
*
|
||||||
|
* @author Jesse Wilson
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
final class Platform {
|
||||||
|
private static final Logger logger = Logger.getLogger(Platform.class.getName());
|
||||||
|
private static final PatternCompiler patternCompiler = loadPatternCompiler();
|
||||||
|
|
||||||
|
private Platform() {}
|
||||||
|
|
||||||
|
/** Calls {@link System#nanoTime()}. */
|
||||||
|
@SuppressWarnings("GoodTime") // reading system time without TimeSource
|
||||||
|
static long systemNanoTime() {
|
||||||
|
return System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharMatcher precomputeCharMatcher(CharMatcher matcher) {
|
||||||
|
return matcher.precomputedInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T extends Enum<T>> Optional<T> getEnumIfPresent(Class<T> enumClass, String value) {
|
||||||
|
WeakReference<? extends Enum<?>> ref = Enums.getEnumConstants(enumClass).get(value);
|
||||||
|
return ref == null ? Optional.<T>absent() : Optional.of(enumClass.cast(ref.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String formatCompact4Digits(double value) {
|
||||||
|
return String.format(Locale.ROOT, "%.4g", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean stringIsNullOrEmpty(String string) {
|
||||||
|
return string == null || string.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String nullToEmpty(String string) {
|
||||||
|
return (string == null) ? "" : string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String emptyToNull(String string) {
|
||||||
|
return stringIsNullOrEmpty(string) ? null : string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommonPattern compilePattern(String pattern) {
|
||||||
|
Preconditions.checkNotNull(pattern);
|
||||||
|
return patternCompiler.compile(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean patternCompilerIsPcreLike() {
|
||||||
|
return patternCompiler.isPcreLike();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PatternCompiler loadPatternCompiler() {
|
||||||
|
return new JdkPatternCompiler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logPatternCompilerError(ServiceConfigurationError e) {
|
||||||
|
logger.log(Level.WARNING, "Error loading regex compiler, falling back to next option", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class JdkPatternCompiler implements PatternCompiler {
|
||||||
|
@Override
|
||||||
|
public CommonPattern compile(String pattern) {
|
||||||
|
return new JdkPattern(Pattern.compile(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPcreLike() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String GWT_RPC_PROPERTY_NAME = "guava.gwt.emergency_reenable_rpc";
|
||||||
|
|
||||||
|
static void checkGwtRpcEnabled() {
|
||||||
|
if (!parseBoolean(System.getProperty(GWT_RPC_PROPERTY_NAME, "true"))) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
lenientFormat(
|
||||||
|
"We are removing GWT-RPC support for Guava types. You can temporarily reenable"
|
||||||
|
+ " support by setting the system property %s to true. For more about system"
|
||||||
|
+ " properties, see %s. For more about Guava's GWT-RPC support, see %s.",
|
||||||
|
GWT_RPC_PROPERTY_NAME,
|
||||||
|
"https://stackoverflow.com/q/5189914/28465",
|
||||||
|
"https://groups.google.com/d/msg/guava-announce/zHZTFg7YF3o/rQNnwdHeEwAJ"));
|
||||||
|
}
|
||||||
|
logger.log(
|
||||||
|
java.util.logging.Level.WARNING,
|
||||||
|
"In January 2020, we will remove GWT-RPC support for Guava types. You are seeing this"
|
||||||
|
+ " warning because you are sending a Guava type over GWT-RPC, which will break. You"
|
||||||
|
+ " can identify which type by looking at the class name in the attached stack trace.",
|
||||||
|
new Throwable());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
1404
src/main/java/com/google/common/base/Preconditions.java
Normal file
1404
src/main/java/com/google/common/base/Preconditions.java
Normal file
File diff suppressed because it is too large
Load diff
78
src/main/java/com/google/common/base/Predicate.java
Normal file
78
src/main/java/com/google/common/base/Predicate.java
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy version of {@link java.util.function.Predicate java.util.function.Predicate}. Determines a
|
||||||
|
* true or false value for a given input.
|
||||||
|
*
|
||||||
|
* <p>As this interface extends {@code java.util.function.Predicate}, an instance of this type may
|
||||||
|
* be used as a {@code Predicate} directly. To use a {@code java.util.function.Predicate} where a
|
||||||
|
* {@code com.google.common.base.Predicate} is expected, use the method reference {@code
|
||||||
|
* predicate::test}.
|
||||||
|
*
|
||||||
|
* <p>This interface is now a legacy type. Use {@code java.util.function.Predicate} (or the
|
||||||
|
* appropriate primitive specialization such as {@code IntPredicate}) instead whenever possible.
|
||||||
|
* Otherwise, at least reduce <i>explicit</i> dependencies on this type by using lambda expressions
|
||||||
|
* or method references instead of classes, leaving your code easier to migrate in the future.
|
||||||
|
*
|
||||||
|
* <p>The {@link Predicates} class provides common predicates and related utilities.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/FunctionalExplained">the use of {@code Predicate}</a>.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
@GwtCompatible
|
||||||
|
public interface Predicate<T> extends java.util.function.Predicate<T> {
|
||||||
|
/**
|
||||||
|
* Returns the result of applying this predicate to {@code input} (Java 8 users, see notes in the
|
||||||
|
* class documentation above). This method is <i>generally expected</i>, but not absolutely
|
||||||
|
* required, to have the following properties:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Its execution does not cause any observable side effects.
|
||||||
|
* <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal
|
||||||
|
* Objects.equal}{@code (a, b)} implies that {@code predicate.apply(a) ==
|
||||||
|
* predicate.apply(b))}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if {@code input} is null and this predicate does not accept null
|
||||||
|
* arguments
|
||||||
|
*/
|
||||||
|
boolean apply(T input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether another object is equal to this predicate.
|
||||||
|
*
|
||||||
|
* <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.
|
||||||
|
* However, an implementation may also choose to return {@code true} whenever {@code object} is a
|
||||||
|
* {@link Predicate} that it considers <i>interchangeable</i> with this one. "Interchangeable"
|
||||||
|
* <i>typically</i> means that {@code this.apply(t) == that.apply(t)} for all {@code t} of type
|
||||||
|
* {@code T}). Note that a {@code false} result from this method does not imply that the
|
||||||
|
* predicates are known <i>not</i> to be interchangeable.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
boolean equals(Object object);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean test(T input) {
|
||||||
|
return apply(input);
|
||||||
|
}
|
||||||
|
}
|
702
src/main/java/com/google/common/base/Predicates.java
Normal file
702
src/main/java/com/google/common/base/Predicates.java
Normal file
|
@ -0,0 +1,702 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods pertaining to {@code Predicate} instances.
|
||||||
|
*
|
||||||
|
* <p>All methods return serializable predicates as long as they're given serializable parameters.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/FunctionalExplained">the use of {@code Predicate}</a>.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public final class Predicates {
|
||||||
|
private Predicates() {}
|
||||||
|
|
||||||
|
// TODO(kevinb): considering having these implement a VisitablePredicate
|
||||||
|
// interface which specifies an accept(PredicateVisitor) method.
|
||||||
|
|
||||||
|
/** Returns a predicate that always evaluates to {@code true}. */
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
public static <T> Predicate<T> alwaysTrue() {
|
||||||
|
return ObjectPredicate.ALWAYS_TRUE.withNarrowedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a predicate that always evaluates to {@code false}. */
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
public static <T> Predicate<T> alwaysFalse() {
|
||||||
|
return ObjectPredicate.ALWAYS_FALSE.withNarrowedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the object reference being tested is
|
||||||
|
* null.
|
||||||
|
*/
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
public static <T> Predicate<T> isNull() {
|
||||||
|
return ObjectPredicate.IS_NULL.withNarrowedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the object reference being tested is not
|
||||||
|
* null.
|
||||||
|
*/
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
public static <T> Predicate<T> notNull() {
|
||||||
|
return ObjectPredicate.NOT_NULL.withNarrowedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the given predicate evaluates to {@code
|
||||||
|
* false}.
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> not(Predicate<T> predicate) {
|
||||||
|
return new NotPredicate<T>(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if each of its components evaluates to
|
||||||
|
* {@code true}. The components are evaluated in order, and evaluation will be "short-circuited"
|
||||||
|
* as soon as a false predicate is found. It defensively copies the iterable passed in, so future
|
||||||
|
* changes to it won't alter the behavior of this predicate. If {@code components} is empty, the
|
||||||
|
* returned predicate will always evaluate to {@code true}.
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> and(Iterable<? extends Predicate<? super T>> components) {
|
||||||
|
return new AndPredicate<T>(defensiveCopy(components));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if each of its components evaluates to
|
||||||
|
* {@code true}. The components are evaluated in order, and evaluation will be "short-circuited"
|
||||||
|
* as soon as a false predicate is found. It defensively copies the array passed in, so future
|
||||||
|
* changes to it won't alter the behavior of this predicate. If {@code components} is empty, the
|
||||||
|
* returned predicate will always evaluate to {@code true}.
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> Predicate<T> and(Predicate<? super T>... components) {
|
||||||
|
return new AndPredicate<T>(defensiveCopy(components));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if both of its components evaluate to {@code
|
||||||
|
* true}. The components are evaluated in order, and evaluation will be "short-circuited" as soon
|
||||||
|
* as a false predicate is found.
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> and(Predicate<? super T> first, Predicate<? super T> second) {
|
||||||
|
return new AndPredicate<T>(Predicates.<T>asList(checkNotNull(first), checkNotNull(second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if any one of its components evaluates to
|
||||||
|
* {@code true}. The components are evaluated in order, and evaluation will be "short-circuited"
|
||||||
|
* as soon as a true predicate is found. It defensively copies the iterable passed in, so future
|
||||||
|
* changes to it won't alter the behavior of this predicate. If {@code components} is empty, the
|
||||||
|
* returned predicate will always evaluate to {@code false}.
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> or(Iterable<? extends Predicate<? super T>> components) {
|
||||||
|
return new OrPredicate<T>(defensiveCopy(components));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if any one of its components evaluates to
|
||||||
|
* {@code true}. The components are evaluated in order, and evaluation will be "short-circuited"
|
||||||
|
* as soon as a true predicate is found. It defensively copies the array passed in, so future
|
||||||
|
* changes to it won't alter the behavior of this predicate. If {@code components} is empty, the
|
||||||
|
* returned predicate will always evaluate to {@code false}.
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> Predicate<T> or(Predicate<? super T>... components) {
|
||||||
|
return new OrPredicate<T>(defensiveCopy(components));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if either of its components evaluates to
|
||||||
|
* {@code true}. The components are evaluated in order, and evaluation will be "short-circuited"
|
||||||
|
* as soon as a true predicate is found.
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> or(Predicate<? super T> first, Predicate<? super T> second) {
|
||||||
|
return new OrPredicate<T>(Predicates.<T>asList(checkNotNull(first), checkNotNull(second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the object being tested {@code equals()}
|
||||||
|
* the given target or both are null.
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> equalTo(T target) {
|
||||||
|
return (target == null) ? Predicates.<T>isNull() : new IsEqualToPredicate<T>(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the object being tested is an instance of
|
||||||
|
* the given class. If the object being tested is {@code null} this predicate evaluates to {@code
|
||||||
|
* false}.
|
||||||
|
*
|
||||||
|
* <p>If you want to filter an {@code Iterable} to narrow its type, consider using {@link
|
||||||
|
* com.google.common.collect.Iterables#filter(Iterable, Class)} in preference.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> contrary to the typical assumptions about predicates (as documented at
|
||||||
|
* {@link Predicate#apply}), the returned predicate may not be <i>consistent with equals</i>. For
|
||||||
|
* example, {@code instanceOf(ArrayList.class)} will yield different results for the two equal
|
||||||
|
* instances {@code Lists.newArrayList(1)} and {@code Arrays.asList(1)}.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Class.isInstance
|
||||||
|
public static Predicate<Object> instanceOf(Class<?> clazz) {
|
||||||
|
return new InstanceOfPredicate(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the class being tested is assignable to
|
||||||
|
* (is a subtype of) {@code clazz}. Example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* List<Class<?>> classes = Arrays.asList(
|
||||||
|
* Object.class, String.class, Number.class, Long.class);
|
||||||
|
* return Iterables.filter(classes, subtypeOf(Number.class));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* The code above returns an iterable containing {@code Number.class} and {@code Long.class}.
|
||||||
|
*
|
||||||
|
* @since 20.0 (since 10.0 under the incorrect name {@code assignableFrom})
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Class.isAssignableFrom
|
||||||
|
@Beta
|
||||||
|
public static Predicate<Class<?>> subtypeOf(Class<?> clazz) {
|
||||||
|
return new SubtypeOfPredicate(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the object reference being tested is a
|
||||||
|
* member of the given collection. It does not defensively copy the collection passed in, so
|
||||||
|
* future changes to it will alter the behavior of the predicate.
|
||||||
|
*
|
||||||
|
* <p>This method can technically accept any {@code Collection<?>}, but using a typed collection
|
||||||
|
* helps prevent bugs. This approach doesn't block any potential users since it is always possible
|
||||||
|
* to use {@code Predicates.<Object>in()}.
|
||||||
|
*
|
||||||
|
* @param target the collection that may contain the function input
|
||||||
|
*/
|
||||||
|
public static <T> Predicate<T> in(Collection<? extends T> target) {
|
||||||
|
return new InPredicate<T>(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the composition of a function and a predicate. For every {@code x}, the generated
|
||||||
|
* predicate returns {@code predicate(function(x))}.
|
||||||
|
*
|
||||||
|
* @return the composition of the provided function and predicate
|
||||||
|
*/
|
||||||
|
public static <A, B> Predicate<A> compose(
|
||||||
|
Predicate<B> predicate, Function<A, ? extends B> function) {
|
||||||
|
return new CompositionPredicate<>(predicate, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the {@code CharSequence} being tested
|
||||||
|
* contains any match for the given regular expression pattern. The test used is equivalent to
|
||||||
|
* {@code Pattern.compile(pattern).matcher(arg).find()}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the pattern is invalid
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Only used by other GWT-incompatible code.
|
||||||
|
public static Predicate<CharSequence> containsPattern(String pattern) {
|
||||||
|
return new ContainsPatternFromStringPredicate(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicate that evaluates to {@code true} if the {@code CharSequence} being tested
|
||||||
|
* contains any match for the given regular expression pattern. The test used is equivalent to
|
||||||
|
* {@code pattern.matcher(arg).find()}
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible(value = "java.util.regex.Pattern")
|
||||||
|
public static Predicate<CharSequence> contains(Pattern pattern) {
|
||||||
|
return new ContainsPatternPredicate(new JdkPattern(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
// End public API, begin private implementation classes.
|
||||||
|
|
||||||
|
// Package private for GWT serialization.
|
||||||
|
enum ObjectPredicate implements Predicate<Object> {
|
||||||
|
/** @see Predicates#alwaysTrue() */
|
||||||
|
ALWAYS_TRUE {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Object o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.alwaysTrue()";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** @see Predicates#alwaysFalse() */
|
||||||
|
ALWAYS_FALSE {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Object o) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.alwaysFalse()";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** @see Predicates#isNull() */
|
||||||
|
IS_NULL {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Object o) {
|
||||||
|
return o == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.isNull()";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** @see Predicates#notNull() */
|
||||||
|
NOT_NULL {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Object o) {
|
||||||
|
return o != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.notNull()";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // safe contravariant cast
|
||||||
|
<T> Predicate<T> withNarrowedType() {
|
||||||
|
return (Predicate<T>) this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#not(Predicate) */
|
||||||
|
private static class NotPredicate<T> implements Predicate<T>, Serializable {
|
||||||
|
final Predicate<T> predicate;
|
||||||
|
|
||||||
|
NotPredicate(Predicate<T> predicate) {
|
||||||
|
this.predicate = checkNotNull(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(T t) {
|
||||||
|
return !predicate.apply(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return ~predicate.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof NotPredicate) {
|
||||||
|
NotPredicate<?> that = (NotPredicate<?>) obj;
|
||||||
|
return predicate.equals(that.predicate);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.not(" + predicate + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#and(Iterable) */
|
||||||
|
private static class AndPredicate<T> implements Predicate<T>, Serializable {
|
||||||
|
private final List<? extends Predicate<? super T>> components;
|
||||||
|
|
||||||
|
private AndPredicate(List<? extends Predicate<? super T>> components) {
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(T t) {
|
||||||
|
// Avoid using the Iterator to avoid generating garbage (issue 820).
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
if (!components.get(i).apply(t)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// add a random number to avoid collisions with OrPredicate
|
||||||
|
return components.hashCode() + 0x12472c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof AndPredicate) {
|
||||||
|
AndPredicate<?> that = (AndPredicate<?>) obj;
|
||||||
|
return components.equals(that.components);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toStringHelper("and", components);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#or(Iterable) */
|
||||||
|
private static class OrPredicate<T> implements Predicate<T>, Serializable {
|
||||||
|
private final List<? extends Predicate<? super T>> components;
|
||||||
|
|
||||||
|
private OrPredicate(List<? extends Predicate<? super T>> components) {
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(T t) {
|
||||||
|
// Avoid using the Iterator to avoid generating garbage (issue 820).
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
if (components.get(i).apply(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// add a random number to avoid collisions with AndPredicate
|
||||||
|
return components.hashCode() + 0x053c91cf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof OrPredicate) {
|
||||||
|
OrPredicate<?> that = (OrPredicate<?>) obj;
|
||||||
|
return components.equals(that.components);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toStringHelper("or", components);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toStringHelper(String methodName, Iterable<?> components) {
|
||||||
|
StringBuilder builder = new StringBuilder("Predicates.").append(methodName).append('(');
|
||||||
|
boolean first = true;
|
||||||
|
for (Object o : components) {
|
||||||
|
if (!first) {
|
||||||
|
builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append(o);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return builder.append(')').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#equalTo(Object) */
|
||||||
|
private static class IsEqualToPredicate<T> implements Predicate<T>, Serializable {
|
||||||
|
private final T target;
|
||||||
|
|
||||||
|
private IsEqualToPredicate(T target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(T t) {
|
||||||
|
return target.equals(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return target.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof IsEqualToPredicate) {
|
||||||
|
IsEqualToPredicate<?> that = (IsEqualToPredicate<?>) obj;
|
||||||
|
return target.equals(that.target);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.equalTo(" + target + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#instanceOf(Class) */
|
||||||
|
@GwtIncompatible // Class.isInstance
|
||||||
|
private static class InstanceOfPredicate implements Predicate<Object>, Serializable {
|
||||||
|
private final Class<?> clazz;
|
||||||
|
|
||||||
|
private InstanceOfPredicate(Class<?> clazz) {
|
||||||
|
this.clazz = checkNotNull(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Object o) {
|
||||||
|
return clazz.isInstance(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return clazz.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof InstanceOfPredicate) {
|
||||||
|
InstanceOfPredicate that = (InstanceOfPredicate) obj;
|
||||||
|
return clazz == that.clazz;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.instanceOf(" + clazz.getName() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#subtypeOf(Class) */
|
||||||
|
@GwtIncompatible // Class.isAssignableFrom
|
||||||
|
private static class SubtypeOfPredicate implements Predicate<Class<?>>, Serializable {
|
||||||
|
private final Class<?> clazz;
|
||||||
|
|
||||||
|
private SubtypeOfPredicate(Class<?> clazz) {
|
||||||
|
this.clazz = checkNotNull(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Class<?> input) {
|
||||||
|
return clazz.isAssignableFrom(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return clazz.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof SubtypeOfPredicate) {
|
||||||
|
SubtypeOfPredicate that = (SubtypeOfPredicate) obj;
|
||||||
|
return clazz == that.clazz;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.subtypeOf(" + clazz.getName() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#in(Collection) */
|
||||||
|
private static class InPredicate<T> implements Predicate<T>, Serializable {
|
||||||
|
private final Collection<?> target;
|
||||||
|
|
||||||
|
private InPredicate(Collection<?> target) {
|
||||||
|
this.target = checkNotNull(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(T t) {
|
||||||
|
try {
|
||||||
|
return target.contains(t);
|
||||||
|
} catch (NullPointerException | ClassCastException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof InPredicate) {
|
||||||
|
InPredicate<?> that = (InPredicate<?>) obj;
|
||||||
|
return target.equals(that.target);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return target.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.in(" + target + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#compose(Predicate, Function) */
|
||||||
|
private static class CompositionPredicate<A, B> implements Predicate<A>, Serializable {
|
||||||
|
final Predicate<B> p;
|
||||||
|
final Function<A, ? extends B> f;
|
||||||
|
|
||||||
|
private CompositionPredicate(Predicate<B> p, Function<A, ? extends B> f) {
|
||||||
|
this.p = checkNotNull(p);
|
||||||
|
this.f = checkNotNull(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(A a) {
|
||||||
|
return p.apply(f.apply(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof CompositionPredicate) {
|
||||||
|
CompositionPredicate<?, ?> that = (CompositionPredicate<?, ?>) obj;
|
||||||
|
return f.equals(that.f) && p.equals(that.p);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return f.hashCode() ^ p.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// TODO(cpovirk): maybe make this look like the method call does ("Predicates.compose(...)")
|
||||||
|
return p + "(" + f + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#contains(Pattern) */
|
||||||
|
@GwtIncompatible // Only used by other GWT-incompatible code.
|
||||||
|
private static class ContainsPatternPredicate implements Predicate<CharSequence>, Serializable {
|
||||||
|
final CommonPattern pattern;
|
||||||
|
|
||||||
|
ContainsPatternPredicate(CommonPattern pattern) {
|
||||||
|
this.pattern = checkNotNull(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(CharSequence t) {
|
||||||
|
return pattern.matcher(t).find();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// Pattern uses Object.hashCode, so we have to reach
|
||||||
|
// inside to build a hashCode consistent with equals.
|
||||||
|
|
||||||
|
return Objects.hashCode(pattern.pattern(), pattern.flags());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ContainsPatternPredicate) {
|
||||||
|
ContainsPatternPredicate that = (ContainsPatternPredicate) obj;
|
||||||
|
|
||||||
|
// Pattern uses Object (identity) equality, so we have to reach
|
||||||
|
// inside to compare individual fields.
|
||||||
|
return Objects.equal(pattern.pattern(), that.pattern.pattern())
|
||||||
|
&& pattern.flags() == that.pattern.flags();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String patternString =
|
||||||
|
MoreObjects.toStringHelper(pattern)
|
||||||
|
.add("pattern", pattern.pattern())
|
||||||
|
.add("pattern.flags", pattern.flags())
|
||||||
|
.toString();
|
||||||
|
return "Predicates.contains(" + patternString + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see Predicates#containsPattern(String) */
|
||||||
|
@GwtIncompatible // Only used by other GWT-incompatible code.
|
||||||
|
private static class ContainsPatternFromStringPredicate extends ContainsPatternPredicate {
|
||||||
|
|
||||||
|
ContainsPatternFromStringPredicate(String string) {
|
||||||
|
super(Platform.compilePattern(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Predicates.containsPattern(" + pattern.pattern() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<Predicate<? super T>> asList(
|
||||||
|
Predicate<? super T> first, Predicate<? super T> second) {
|
||||||
|
// TODO(kevinb): understand why we still get a warning despite @SafeVarargs!
|
||||||
|
return Arrays.<Predicate<? super T>>asList(first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> defensiveCopy(T... array) {
|
||||||
|
return defensiveCopy(Arrays.asList(array));
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> List<T> defensiveCopy(Iterable<T> iterable) {
|
||||||
|
ArrayList<T> list = new ArrayList<T>();
|
||||||
|
for (T element : iterable) {
|
||||||
|
list.add(checkNotNull(element));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
98
src/main/java/com/google/common/base/Present.java
Normal file
98
src/main/java/com/google/common/base/Present.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Implementation of an {@link Optional} containing a reference. */
|
||||||
|
@GwtCompatible
|
||||||
|
final class Present<T> extends Optional<T> {
|
||||||
|
private final T reference;
|
||||||
|
|
||||||
|
Present(T reference) {
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPresent() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T or(T defaultValue) {
|
||||||
|
checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)");
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<T> or(Optional<? extends T> secondChoice) {
|
||||||
|
checkNotNull(secondChoice);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T or(Supplier<? extends T> supplier) {
|
||||||
|
checkNotNull(supplier);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T orNull() {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<T> asSet() {
|
||||||
|
return Collections.singleton(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> Optional<V> transform(Function<? super T, V> function) {
|
||||||
|
return new Present<V>(
|
||||||
|
checkNotNull(
|
||||||
|
function.apply(reference),
|
||||||
|
"the Function passed to Optional.transform() must not return null."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof Present) {
|
||||||
|
Present<?> other = (Present<?>) object;
|
||||||
|
return reference.equals(other.reference);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0x598df91c + reference.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Optional.of(" + reference + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
146
src/main/java/com/google/common/base/SmallCharMatcher.java
Normal file
146
src/main/java/com/google/common/base/SmallCharMatcher.java
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.CharMatcher.NamedFastMatcher;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable version of CharMatcher for smallish sets of characters that uses a hash table with
|
||||||
|
* linear probing to check for matches.
|
||||||
|
*
|
||||||
|
* @author Christopher Swenson
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // no precomputation is done in GWT
|
||||||
|
final class SmallCharMatcher extends NamedFastMatcher {
|
||||||
|
static final int MAX_SIZE = 1023;
|
||||||
|
private final char[] table;
|
||||||
|
private final boolean containsZero;
|
||||||
|
private final long filter;
|
||||||
|
|
||||||
|
private SmallCharMatcher(char[] table, long filter, boolean containsZero, String description) {
|
||||||
|
super(description);
|
||||||
|
this.table = table;
|
||||||
|
this.filter = filter;
|
||||||
|
this.containsZero = containsZero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int C1 = 0xcc9e2d51;
|
||||||
|
private static final int C2 = 0x1b873593;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method was rewritten in Java from an intermediate step of the Murmur hash function in
|
||||||
|
* http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the
|
||||||
|
* following header:
|
||||||
|
*
|
||||||
|
* MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author
|
||||||
|
* hereby disclaims copyright to this source code.
|
||||||
|
*/
|
||||||
|
static int smear(int hashCode) {
|
||||||
|
return C2 * Integer.rotateLeft(hashCode * C1, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkFilter(int c) {
|
||||||
|
return 1 == (1 & (filter >> c));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is all essentially copied from ImmutableSet, but we have to duplicate because
|
||||||
|
// of dependencies.
|
||||||
|
|
||||||
|
// Represents how tightly we can pack things, as a maximum.
|
||||||
|
private static final double DESIRED_LOAD_FACTOR = 0.5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array size suitable for the backing array of a hash table that uses open addressing
|
||||||
|
* with linear probing in its implementation. The returned size is the smallest power of two that
|
||||||
|
* can hold setSize elements with the desired load factor.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
static int chooseTableSize(int setSize) {
|
||||||
|
if (setSize == 1) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
// Correct the size for open addressing to match desired load factor.
|
||||||
|
// Round up to the next highest power of 2.
|
||||||
|
int tableSize = Integer.highestOneBit(setSize - 1) << 1;
|
||||||
|
while (tableSize * DESIRED_LOAD_FACTOR < setSize) {
|
||||||
|
tableSize <<= 1;
|
||||||
|
}
|
||||||
|
return tableSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharMatcher from(BitSet chars, String description) {
|
||||||
|
// Compute the filter.
|
||||||
|
long filter = 0;
|
||||||
|
int size = chars.cardinality();
|
||||||
|
boolean containsZero = chars.get(0);
|
||||||
|
// Compute the hash table.
|
||||||
|
char[] table = new char[chooseTableSize(size)];
|
||||||
|
int mask = table.length - 1;
|
||||||
|
for (int c = chars.nextSetBit(0); c != -1; c = chars.nextSetBit(c + 1)) {
|
||||||
|
// Compute the filter at the same time.
|
||||||
|
filter |= 1L << c;
|
||||||
|
int index = smear(c) & mask;
|
||||||
|
while (true) {
|
||||||
|
// Check for empty.
|
||||||
|
if (table[index] == 0) {
|
||||||
|
table[index] = (char) c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Linear probing.
|
||||||
|
index = (index + 1) & mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new SmallCharMatcher(table, filter, containsZero, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(char c) {
|
||||||
|
if (c == 0) {
|
||||||
|
return containsZero;
|
||||||
|
}
|
||||||
|
if (!checkFilter(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int mask = table.length - 1;
|
||||||
|
int startingIndex = smear(c) & mask;
|
||||||
|
int index = startingIndex;
|
||||||
|
do {
|
||||||
|
if (table[index] == 0) { // Check for empty.
|
||||||
|
return false;
|
||||||
|
} else if (table[index] == c) { // Check for match.
|
||||||
|
return true;
|
||||||
|
} else { // Linear probing.
|
||||||
|
index = (index + 1) & mask;
|
||||||
|
}
|
||||||
|
// Check to see if we wrapped around the whole table.
|
||||||
|
} while (index != startingIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setBits(BitSet table) {
|
||||||
|
if (containsZero) {
|
||||||
|
table.set(0);
|
||||||
|
}
|
||||||
|
for (char c : this.table) {
|
||||||
|
if (c != 0) {
|
||||||
|
table.set(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
633
src/main/java/com/google/common/base/Splitter.java
Normal file
633
src/main/java/com/google/common/base/Splitter.java
Normal file
|
@ -0,0 +1,633 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts non-overlapping substrings from an input string, typically by recognizing appearances of
|
||||||
|
* a <i>separator</i> sequence. This separator can be specified as a single {@linkplain #on(char)
|
||||||
|
* character}, fixed {@linkplain #on(String) string}, {@linkplain #onPattern regular expression} or
|
||||||
|
* {@link #on(CharMatcher) CharMatcher} instance. Or, instead of using a separator at all, a
|
||||||
|
* splitter can extract adjacent substrings of a given {@linkplain #fixedLength fixed length}.
|
||||||
|
*
|
||||||
|
* <p>For example, this expression:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Splitter.on(',').split("foo,bar,qux")
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* ... produces an {@code Iterable} containing {@code "foo"}, {@code "bar"} and {@code "qux"}, in
|
||||||
|
* that order.
|
||||||
|
*
|
||||||
|
* <p>By default, {@code Splitter}'s behavior is simplistic and unassuming. The following
|
||||||
|
* expression:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Splitter.on(',').split(" foo,,, bar ,")
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* ... yields the substrings {@code [" foo", "", "", " bar ", ""]}. If this is not the desired
|
||||||
|
* behavior, use configuration methods to obtain a <i>new</i> splitter instance with modified
|
||||||
|
* behavior:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* private static final Splitter MY_SPLITTER = Splitter.on(',')
|
||||||
|
* .trimResults()
|
||||||
|
* .omitEmptyStrings();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Now {@code MY_SPLITTER.split("foo,,, bar ,")} returns just {@code ["foo", "bar"]}. Note that
|
||||||
|
* the order in which these configuration methods are called is never significant.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> Splitter instances are immutable. Invoking a configuration method has no
|
||||||
|
* effect on the receiving instance; you must store and use the new splitter instance it returns
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* // Do NOT do this
|
||||||
|
* Splitter splitter = Splitter.on('/');
|
||||||
|
* splitter.trimResults(); // does nothing!
|
||||||
|
* return splitter.split("wrong / wrong / wrong");
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>For separator-based splitters that do not use {@code omitEmptyStrings}, an input string
|
||||||
|
* containing {@code n} occurrences of the separator naturally yields an iterable of size {@code n +
|
||||||
|
* 1}. So if the separator does not occur anywhere in the input, a single substring is returned
|
||||||
|
* containing the entire input. Consequently, all splitters split the empty string to {@code [""]}
|
||||||
|
* (note: even fixed-length splitters).
|
||||||
|
*
|
||||||
|
* <p>Splitter instances are thread-safe immutable, and are therefore safe to store as {@code static
|
||||||
|
* final} constants.
|
||||||
|
*
|
||||||
|
* <p>The {@link Joiner} class provides the inverse operation to splitting, but note that a
|
||||||
|
* round-trip between the two should be assumed to be lossy.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/StringsExplained#splitter">{@code Splitter}</a>.
|
||||||
|
*
|
||||||
|
* @author Julien Silland
|
||||||
|
* @author Jesse Wilson
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @author Louis Wasserman
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public final class Splitter {
|
||||||
|
private final CharMatcher trimmer;
|
||||||
|
private final boolean omitEmptyStrings;
|
||||||
|
private final Strategy strategy;
|
||||||
|
private final int limit;
|
||||||
|
|
||||||
|
private Splitter(Strategy strategy) {
|
||||||
|
this(strategy, false, CharMatcher.none(), Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Splitter(Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) {
|
||||||
|
this.strategy = strategy;
|
||||||
|
this.omitEmptyStrings = omitEmptyStrings;
|
||||||
|
this.trimmer = trimmer;
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that uses the given single-character separator. For example, {@code
|
||||||
|
* Splitter.on(',').split("foo,,bar")} returns an iterable containing {@code ["foo", "", "bar"]}.
|
||||||
|
*
|
||||||
|
* @param separator the character to recognize as a separator
|
||||||
|
* @return a splitter, with default settings, that recognizes that separator
|
||||||
|
*/
|
||||||
|
public static Splitter on(char separator) {
|
||||||
|
return on(CharMatcher.is(separator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that considers any single character matched by the given {@code CharMatcher}
|
||||||
|
* to be a separator. For example, {@code
|
||||||
|
* Splitter.on(CharMatcher.anyOf(";,")).split("foo,;bar,quux")} returns an iterable containing
|
||||||
|
* {@code ["foo", "", "bar", "quux"]}.
|
||||||
|
*
|
||||||
|
* @param separatorMatcher a {@link CharMatcher} that determines whether a character is a
|
||||||
|
* separator
|
||||||
|
* @return a splitter, with default settings, that uses this matcher
|
||||||
|
*/
|
||||||
|
public static Splitter on(final CharMatcher separatorMatcher) {
|
||||||
|
checkNotNull(separatorMatcher);
|
||||||
|
|
||||||
|
return new Splitter(
|
||||||
|
new Strategy() {
|
||||||
|
@Override
|
||||||
|
public SplittingIterator iterator(Splitter splitter, final CharSequence toSplit) {
|
||||||
|
return new SplittingIterator(splitter, toSplit) {
|
||||||
|
@Override
|
||||||
|
int separatorStart(int start) {
|
||||||
|
return separatorMatcher.indexIn(toSplit, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int separatorEnd(int separatorPosition) {
|
||||||
|
return separatorPosition + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that uses the given fixed string as a separator. For example, {@code
|
||||||
|
* Splitter.on(", ").split("foo, bar,baz")} returns an iterable containing {@code ["foo",
|
||||||
|
* "bar,baz"]}.
|
||||||
|
*
|
||||||
|
* @param separator the literal, nonempty string to recognize as a separator
|
||||||
|
* @return a splitter, with default settings, that recognizes that separator
|
||||||
|
*/
|
||||||
|
public static Splitter on(final String separator) {
|
||||||
|
checkArgument(separator.length() != 0, "The separator may not be the empty string.");
|
||||||
|
if (separator.length() == 1) {
|
||||||
|
return Splitter.on(separator.charAt(0));
|
||||||
|
}
|
||||||
|
return new Splitter(
|
||||||
|
new Strategy() {
|
||||||
|
@Override
|
||||||
|
public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) {
|
||||||
|
return new SplittingIterator(splitter, toSplit) {
|
||||||
|
@Override
|
||||||
|
public int separatorStart(int start) {
|
||||||
|
int separatorLength = separator.length();
|
||||||
|
|
||||||
|
positions:
|
||||||
|
for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) {
|
||||||
|
for (int i = 0; i < separatorLength; i++) {
|
||||||
|
if (toSplit.charAt(i + p) != separator.charAt(i)) {
|
||||||
|
continue positions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int separatorEnd(int separatorPosition) {
|
||||||
|
return separatorPosition + separator.length();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that considers any subsequence matching {@code pattern} to be a separator.
|
||||||
|
* For example, {@code Splitter.on(Pattern.compile("\r?\n")).split(entireFile)} splits a string
|
||||||
|
* into lines whether it uses DOS-style or UNIX-style line terminators.
|
||||||
|
*
|
||||||
|
* @param separatorPattern the pattern that determines whether a subsequence is a separator. This
|
||||||
|
* pattern may not match the empty string.
|
||||||
|
* @return a splitter, with default settings, that uses this pattern
|
||||||
|
* @throws IllegalArgumentException if {@code separatorPattern} matches the empty string
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.util.regex
|
||||||
|
public static Splitter on(Pattern separatorPattern) {
|
||||||
|
return on(new JdkPattern(separatorPattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Splitter on(final CommonPattern separatorPattern) {
|
||||||
|
checkArgument(
|
||||||
|
!separatorPattern.matcher("").matches(),
|
||||||
|
"The pattern may not match the empty string: %s",
|
||||||
|
separatorPattern);
|
||||||
|
|
||||||
|
return new Splitter(
|
||||||
|
new Strategy() {
|
||||||
|
@Override
|
||||||
|
public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
|
||||||
|
final CommonMatcher matcher = separatorPattern.matcher(toSplit);
|
||||||
|
return new SplittingIterator(splitter, toSplit) {
|
||||||
|
@Override
|
||||||
|
public int separatorStart(int start) {
|
||||||
|
return matcher.find(start) ? matcher.start() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int separatorEnd(int separatorPosition) {
|
||||||
|
return matcher.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that considers any subsequence matching a given pattern (regular expression)
|
||||||
|
* to be a separator. For example, {@code Splitter.onPattern("\r?\n").split(entireFile)} splits a
|
||||||
|
* string into lines whether it uses DOS-style or UNIX-style line terminators. This is equivalent
|
||||||
|
* to {@code Splitter.on(Pattern.compile(pattern))}.
|
||||||
|
*
|
||||||
|
* @param separatorPattern the pattern that determines whether a subsequence is a separator. This
|
||||||
|
* pattern may not match the empty string.
|
||||||
|
* @return a splitter, with default settings, that uses this pattern
|
||||||
|
* @throws IllegalArgumentException if {@code separatorPattern} matches the empty string or is a
|
||||||
|
* malformed expression
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.util.regex
|
||||||
|
public static Splitter onPattern(String separatorPattern) {
|
||||||
|
return on(Platform.compilePattern(separatorPattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that divides strings into pieces of the given length. For example, {@code
|
||||||
|
* Splitter.fixedLength(2).split("abcde")} returns an iterable containing {@code ["ab", "cd",
|
||||||
|
* "e"]}. The last piece can be smaller than {@code length} but will never be empty.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> if {@link #fixedLength} is used in conjunction with {@link #limit}, the final
|
||||||
|
* split piece <i>may be longer than the specified fixed length</i>. This is because the splitter
|
||||||
|
* will <i>stop splitting when the limit is reached</i>, and just return the final piece as-is.
|
||||||
|
*
|
||||||
|
* <p><b>Exception:</b> for consistency with separator-based splitters, {@code split("")} does not
|
||||||
|
* yield an empty iterable, but an iterable containing {@code ""}. This is the only case in which
|
||||||
|
* {@code Iterables.size(split(input))} does not equal {@code IntMath.divide(input.length(),
|
||||||
|
* length, CEILING)}. To avoid this behavior, use {@code omitEmptyStrings}.
|
||||||
|
*
|
||||||
|
* @param length the desired length of pieces after splitting, a positive integer
|
||||||
|
* @return a splitter, with default settings, that can split into fixed sized pieces
|
||||||
|
* @throws IllegalArgumentException if {@code length} is zero or negative
|
||||||
|
*/
|
||||||
|
public static Splitter fixedLength(final int length) {
|
||||||
|
checkArgument(length > 0, "The length may not be less than 1");
|
||||||
|
|
||||||
|
return new Splitter(
|
||||||
|
new Strategy() {
|
||||||
|
@Override
|
||||||
|
public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
|
||||||
|
return new SplittingIterator(splitter, toSplit) {
|
||||||
|
@Override
|
||||||
|
public int separatorStart(int start) {
|
||||||
|
int nextChunkStart = start + length;
|
||||||
|
return (nextChunkStart < toSplit.length() ? nextChunkStart : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int separatorEnd(int separatorPosition) {
|
||||||
|
return separatorPosition;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that behaves equivalently to {@code this} splitter, but automatically omits
|
||||||
|
* empty strings from the results. For example, {@code
|
||||||
|
* Splitter.on(',').omitEmptyStrings().split(",a,,,b,c,,")} returns an iterable containing only
|
||||||
|
* {@code ["a", "b", "c"]}.
|
||||||
|
*
|
||||||
|
* <p>If either {@code trimResults} option is also specified when creating a splitter, that
|
||||||
|
* splitter always trims results first before checking for emptiness. So, for example, {@code
|
||||||
|
* Splitter.on(':').omitEmptyStrings().trimResults().split(": : : ")} returns an empty iterable.
|
||||||
|
*
|
||||||
|
* <p>Note that it is ordinarily not possible for {@link #split(CharSequence)} to return an empty
|
||||||
|
* iterable, but when using this option, it can (if the input sequence consists of nothing but
|
||||||
|
* separators).
|
||||||
|
*
|
||||||
|
* @return a splitter with the desired configuration
|
||||||
|
*/
|
||||||
|
public Splitter omitEmptyStrings() {
|
||||||
|
return new Splitter(strategy, true, trimmer, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that behaves equivalently to {@code this} splitter but stops splitting after
|
||||||
|
* it reaches the limit. The limit defines the maximum number of items returned by the iterator,
|
||||||
|
* or the maximum size of the list returned by {@link #splitToList}.
|
||||||
|
*
|
||||||
|
* <p>For example, {@code Splitter.on(',').limit(3).split("a,b,c,d")} returns an iterable
|
||||||
|
* containing {@code ["a", "b", "c,d"]}. When omitting empty strings, the omitted strings do not
|
||||||
|
* count. Hence, {@code Splitter.on(',').limit(3).omitEmptyStrings().split("a,,,b,,,c,d")} returns
|
||||||
|
* an iterable containing {@code ["a", "b", "c,d"}. When trim is requested, all entries are
|
||||||
|
* trimmed, including the last. Hence {@code Splitter.on(',').limit(3).trimResults().split(" a , b
|
||||||
|
* , c , d ")} results in {@code ["a", "b", "c , d"]}.
|
||||||
|
*
|
||||||
|
* @param maxItems the maximum number of items returned
|
||||||
|
* @return a splitter with the desired configuration
|
||||||
|
* @since 9.0
|
||||||
|
*/
|
||||||
|
public Splitter limit(int maxItems) {
|
||||||
|
checkArgument(maxItems > 0, "must be greater than zero: %s", maxItems);
|
||||||
|
return new Splitter(strategy, omitEmptyStrings, trimmer, maxItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that behaves equivalently to {@code this} splitter, but automatically
|
||||||
|
* removes leading and trailing {@linkplain CharMatcher#whitespace whitespace} from each returned
|
||||||
|
* substring; equivalent to {@code trimResults(CharMatcher.whitespace())}. For example, {@code
|
||||||
|
* Splitter.on(',').trimResults().split(" a, b ,c ")} returns an iterable containing {@code ["a",
|
||||||
|
* "b", "c"]}.
|
||||||
|
*
|
||||||
|
* @return a splitter with the desired configuration
|
||||||
|
*/
|
||||||
|
public Splitter trimResults() {
|
||||||
|
return trimResults(CharMatcher.whitespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a splitter that behaves equivalently to {@code this} splitter, but removes all leading
|
||||||
|
* or trailing characters matching the given {@code CharMatcher} from each returned substring. For
|
||||||
|
* example, {@code Splitter.on(',').trimResults(CharMatcher.is('_')).split("_a ,_b_ ,c__")}
|
||||||
|
* returns an iterable containing {@code ["a ", "b_ ", "c"]}.
|
||||||
|
*
|
||||||
|
* @param trimmer a {@link CharMatcher} that determines whether a character should be removed from
|
||||||
|
* the beginning/end of a subsequence
|
||||||
|
* @return a splitter with the desired configuration
|
||||||
|
*/
|
||||||
|
// TODO(kevinb): throw if a trimmer was already specified!
|
||||||
|
public Splitter trimResults(CharMatcher trimmer) {
|
||||||
|
checkNotNull(trimmer);
|
||||||
|
return new Splitter(strategy, omitEmptyStrings, trimmer, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits {@code sequence} into string components and makes them available through an {@link
|
||||||
|
* Iterator}, which may be lazily evaluated. If you want an eagerly computed {@link List}, use
|
||||||
|
* {@link #splitToList(CharSequence)}. Java 8 users may prefer {@link #splitToStream} instead.
|
||||||
|
*
|
||||||
|
* @param sequence the sequence of characters to split
|
||||||
|
* @return an iteration over the segments split from the parameter
|
||||||
|
*/
|
||||||
|
public Iterable<String> split(final CharSequence sequence) {
|
||||||
|
checkNotNull(sequence);
|
||||||
|
|
||||||
|
return new Iterable<String>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<String> iterator() {
|
||||||
|
return splittingIterator(sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Joiner.on(", ")
|
||||||
|
.appendTo(new StringBuilder().append('['), this)
|
||||||
|
.append(']')
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterator<String> splittingIterator(CharSequence sequence) {
|
||||||
|
return strategy.iterator(this, sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits {@code sequence} into string components and returns them as an immutable list. If you
|
||||||
|
* want an {@link Iterable} which may be lazily evaluated, use {@link #split(CharSequence)}.
|
||||||
|
*
|
||||||
|
* @param sequence the sequence of characters to split
|
||||||
|
* @return an immutable list of the segments split from the parameter
|
||||||
|
* @since 15.0
|
||||||
|
*/
|
||||||
|
public List<String> splitToList(CharSequence sequence) {
|
||||||
|
checkNotNull(sequence);
|
||||||
|
|
||||||
|
Iterator<String> iterator = splittingIterator(sequence);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
result.add(iterator.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits {@code sequence} into string components and makes them available through an {@link
|
||||||
|
* Stream}, which may be lazily evaluated. If you want an eagerly computed {@link List}, use
|
||||||
|
* {@link #splitToList(CharSequence)}.
|
||||||
|
*
|
||||||
|
* @param sequence the sequence of characters to split
|
||||||
|
* @return a stream over the segments split from the parameter
|
||||||
|
* @since NEXT
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public Stream<String> splitToStream(CharSequence sequence) {
|
||||||
|
// Can't use Streams.stream() from base
|
||||||
|
return StreamSupport.stream(split(sequence).spliterator(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries
|
||||||
|
* into keys and values using the specified separator.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public MapSplitter withKeyValueSeparator(String separator) {
|
||||||
|
return withKeyValueSeparator(on(separator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries
|
||||||
|
* into keys and values using the specified separator.
|
||||||
|
*
|
||||||
|
* @since 14.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public MapSplitter withKeyValueSeparator(char separator) {
|
||||||
|
return withKeyValueSeparator(on(separator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries
|
||||||
|
* into keys and values using the specified key-value splitter.
|
||||||
|
*
|
||||||
|
* <p>Note: Any configuration option configured on this splitter, such as {@link #trimResults},
|
||||||
|
* does not change the behavior of the {@code keyValueSplitter}.
|
||||||
|
*
|
||||||
|
* <p>Example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* String toSplit = " x -> y, z-> a ";
|
||||||
|
* Splitter outerSplitter = Splitter.on(',').trimResults();
|
||||||
|
* MapSplitter mapSplitter = outerSplitter.withKeyValueSeparator(Splitter.on("->"));
|
||||||
|
* Map<String, String> result = mapSplitter.split(toSplit);
|
||||||
|
* assertThat(result).isEqualTo(ImmutableMap.of("x ", " y", "z", " a"));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) {
|
||||||
|
return new MapSplitter(this, keyValueSplitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that splits strings into maps as {@code Splitter} splits iterables and lists. Like
|
||||||
|
* {@code Splitter}, it is thread-safe and immutable. The common way to build instances is by
|
||||||
|
* providing an additional {@linkplain Splitter#withKeyValueSeparator key-value separator} to
|
||||||
|
* {@link Splitter}.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public static final class MapSplitter {
|
||||||
|
private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry";
|
||||||
|
private final Splitter outerSplitter;
|
||||||
|
private final Splitter entrySplitter;
|
||||||
|
|
||||||
|
private MapSplitter(Splitter outerSplitter, Splitter entrySplitter) {
|
||||||
|
this.outerSplitter = outerSplitter; // only "this" is passed
|
||||||
|
this.entrySplitter = checkNotNull(entrySplitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits {@code sequence} into substrings, splits each substring into an entry, and returns an
|
||||||
|
* unmodifiable map with each of the entries. For example, {@code
|
||||||
|
* Splitter.on(';').trimResults().withKeyValueSeparator("=>").split("a=>b ; c=>b")} will return
|
||||||
|
* a mapping from {@code "a"} to {@code "b"} and {@code "c"} to {@code "b"}.
|
||||||
|
*
|
||||||
|
* <p>The returned map preserves the order of the entries from {@code sequence}.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the specified sequence does not split into valid map
|
||||||
|
* entries, or if there are duplicate keys
|
||||||
|
*/
|
||||||
|
public Map<String, String> split(CharSequence sequence) {
|
||||||
|
Map<String, String> map = new LinkedHashMap<>();
|
||||||
|
for (String entry : outerSplitter.split(sequence)) {
|
||||||
|
Iterator<String> entryFields = entrySplitter.splittingIterator(entry);
|
||||||
|
|
||||||
|
checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
|
||||||
|
String key = entryFields.next();
|
||||||
|
checkArgument(!map.containsKey(key), "Duplicate key [%s] found.", key);
|
||||||
|
|
||||||
|
checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
|
||||||
|
String value = entryFields.next();
|
||||||
|
map.put(key, value);
|
||||||
|
|
||||||
|
checkArgument(!entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Strategy {
|
||||||
|
Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class SplittingIterator extends AbstractIterator<String> {
|
||||||
|
final CharSequence toSplit;
|
||||||
|
final CharMatcher trimmer;
|
||||||
|
final boolean omitEmptyStrings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first index in {@code toSplit} at or after {@code start} that contains the
|
||||||
|
* separator.
|
||||||
|
*/
|
||||||
|
abstract int separatorStart(int start);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first index in {@code toSplit} after {@code separatorPosition} that does not
|
||||||
|
* contain a separator. This method is only invoked after a call to {@code separatorStart}.
|
||||||
|
*/
|
||||||
|
abstract int separatorEnd(int separatorPosition);
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
int limit;
|
||||||
|
|
||||||
|
protected SplittingIterator(Splitter splitter, CharSequence toSplit) {
|
||||||
|
this.trimmer = splitter.trimmer;
|
||||||
|
this.omitEmptyStrings = splitter.omitEmptyStrings;
|
||||||
|
this.limit = splitter.limit;
|
||||||
|
this.toSplit = toSplit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String computeNext() {
|
||||||
|
/*
|
||||||
|
* The returned string will be from the end of the last match to the beginning of the next
|
||||||
|
* one. nextStart is the start position of the returned substring, while offset is the place
|
||||||
|
* to start looking for a separator.
|
||||||
|
*/
|
||||||
|
int nextStart = offset;
|
||||||
|
while (offset != -1) {
|
||||||
|
int start = nextStart;
|
||||||
|
int end;
|
||||||
|
|
||||||
|
int separatorPosition = separatorStart(offset);
|
||||||
|
if (separatorPosition == -1) {
|
||||||
|
end = toSplit.length();
|
||||||
|
offset = -1;
|
||||||
|
} else {
|
||||||
|
end = separatorPosition;
|
||||||
|
offset = separatorEnd(separatorPosition);
|
||||||
|
}
|
||||||
|
if (offset == nextStart) {
|
||||||
|
/*
|
||||||
|
* This occurs when some pattern has an empty match, even if it doesn't match the empty
|
||||||
|
* string -- for example, if it requires lookahead or the like. The offset must be
|
||||||
|
* increased to look for separators beyond this point, without changing the start position
|
||||||
|
* of the next returned substring -- so nextStart stays the same.
|
||||||
|
*/
|
||||||
|
offset++;
|
||||||
|
if (offset > toSplit.length()) {
|
||||||
|
offset = -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (start < end && trimmer.matches(toSplit.charAt(start))) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (omitEmptyStrings && start == end) {
|
||||||
|
// Don't include the (unused) separator in next split string.
|
||||||
|
nextStart = offset;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit == 1) {
|
||||||
|
// The limit has been reached, return the rest of the string as the
|
||||||
|
// final item. This is tested after empty string removal so that
|
||||||
|
// empty strings do not count towards the limit.
|
||||||
|
end = toSplit.length();
|
||||||
|
offset = -1;
|
||||||
|
// Since we may have changed the end, we need to trim it again.
|
||||||
|
while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
limit--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toSplit.subSequence(start, end).toString();
|
||||||
|
}
|
||||||
|
return endOfData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
src/main/java/com/google/common/base/StandardSystemProperty.java
Normal file
136
src/main/java/com/google/common/base/StandardSystemProperty.java
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a {@linkplain System#getProperties() standard system property}.
|
||||||
|
*
|
||||||
|
* @author Kurt Alfred Kluever
|
||||||
|
* @since 15.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.lang.System#getProperty
|
||||||
|
public enum StandardSystemProperty {
|
||||||
|
|
||||||
|
/** Java Runtime Environment version. */
|
||||||
|
JAVA_VERSION("java.version"),
|
||||||
|
|
||||||
|
/** Java Runtime Environment vendor. */
|
||||||
|
JAVA_VENDOR("java.vendor"),
|
||||||
|
|
||||||
|
/** Java vendor URL. */
|
||||||
|
JAVA_VENDOR_URL("java.vendor.url"),
|
||||||
|
|
||||||
|
/** Java installation directory. */
|
||||||
|
JAVA_HOME("java.home"),
|
||||||
|
|
||||||
|
/** Java Virtual Machine specification version. */
|
||||||
|
JAVA_VM_SPECIFICATION_VERSION("java.vm.specification.version"),
|
||||||
|
|
||||||
|
/** Java Virtual Machine specification vendor. */
|
||||||
|
JAVA_VM_SPECIFICATION_VENDOR("java.vm.specification.vendor"),
|
||||||
|
|
||||||
|
/** Java Virtual Machine specification name. */
|
||||||
|
JAVA_VM_SPECIFICATION_NAME("java.vm.specification.name"),
|
||||||
|
|
||||||
|
/** Java Virtual Machine implementation version. */
|
||||||
|
JAVA_VM_VERSION("java.vm.version"),
|
||||||
|
|
||||||
|
/** Java Virtual Machine implementation vendor. */
|
||||||
|
JAVA_VM_VENDOR("java.vm.vendor"),
|
||||||
|
|
||||||
|
/** Java Virtual Machine implementation name. */
|
||||||
|
JAVA_VM_NAME("java.vm.name"),
|
||||||
|
|
||||||
|
/** Java Runtime Environment specification version. */
|
||||||
|
JAVA_SPECIFICATION_VERSION("java.specification.version"),
|
||||||
|
|
||||||
|
/** Java Runtime Environment specification vendor. */
|
||||||
|
JAVA_SPECIFICATION_VENDOR("java.specification.vendor"),
|
||||||
|
|
||||||
|
/** Java Runtime Environment specification name. */
|
||||||
|
JAVA_SPECIFICATION_NAME("java.specification.name"),
|
||||||
|
|
||||||
|
/** Java class format version number. */
|
||||||
|
JAVA_CLASS_VERSION("java.class.version"),
|
||||||
|
|
||||||
|
/** Java class path. */
|
||||||
|
JAVA_CLASS_PATH("java.class.path"),
|
||||||
|
|
||||||
|
/** List of paths to search when loading libraries. */
|
||||||
|
JAVA_LIBRARY_PATH("java.library.path"),
|
||||||
|
|
||||||
|
/** Default temp file path. */
|
||||||
|
JAVA_IO_TMPDIR("java.io.tmpdir"),
|
||||||
|
|
||||||
|
/** Name of JIT compiler to use. */
|
||||||
|
JAVA_COMPILER("java.compiler"),
|
||||||
|
|
||||||
|
/** Path of extension directory or directories. */
|
||||||
|
JAVA_EXT_DIRS("java.ext.dirs"),
|
||||||
|
|
||||||
|
/** Operating system name. */
|
||||||
|
OS_NAME("os.name"),
|
||||||
|
|
||||||
|
/** Operating system architecture. */
|
||||||
|
OS_ARCH("os.arch"),
|
||||||
|
|
||||||
|
/** Operating system version. */
|
||||||
|
OS_VERSION("os.version"),
|
||||||
|
|
||||||
|
/** File separator ("/" on UNIX). */
|
||||||
|
FILE_SEPARATOR("file.separator"),
|
||||||
|
|
||||||
|
/** Path separator (":" on UNIX). */
|
||||||
|
PATH_SEPARATOR("path.separator"),
|
||||||
|
|
||||||
|
/** Line separator ("\n" on UNIX). */
|
||||||
|
LINE_SEPARATOR("line.separator"),
|
||||||
|
|
||||||
|
/** User's account name. */
|
||||||
|
USER_NAME("user.name"),
|
||||||
|
|
||||||
|
/** User's home directory. */
|
||||||
|
USER_HOME("user.home"),
|
||||||
|
|
||||||
|
/** User's current working directory. */
|
||||||
|
USER_DIR("user.dir");
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
StandardSystemProperty(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the key used to lookup this system property. */
|
||||||
|
public String key() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current value for this system property by delegating to {@link
|
||||||
|
* System#getProperty(String)}.
|
||||||
|
*/
|
||||||
|
public String value() {
|
||||||
|
return System.getProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a string representation of this system property. */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return key() + "=" + value();
|
||||||
|
}
|
||||||
|
}
|
265
src/main/java/com/google/common/base/Stopwatch.java
Normal file
265
src/main/java/com/google/common/base/Stopwatch.java
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static java.util.concurrent.TimeUnit.DAYS;
|
||||||
|
import static java.util.concurrent.TimeUnit.HOURS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MICROSECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that measures elapsed time in nanoseconds. It is useful to measure elapsed time using
|
||||||
|
* this class instead of direct calls to {@link System#nanoTime} for a few reasons:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>An alternate time source can be substituted, for testing or performance reasons.
|
||||||
|
* <li>As documented by {@code nanoTime}, the value returned has no absolute meaning, and can only
|
||||||
|
* be interpreted as relative to another timestamp returned by {@code nanoTime} at a different
|
||||||
|
* time. {@code Stopwatch} is a more effective abstraction because it exposes only these
|
||||||
|
* relative values, not the absolute ones.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Basic usage:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
|
* doSomething();
|
||||||
|
* stopwatch.stop(); // optional
|
||||||
|
*
|
||||||
|
* Duration duration = stopwatch.elapsed();
|
||||||
|
*
|
||||||
|
* log.info("time: " + stopwatch); // formatted string like "12.3 ms"
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Stopwatch methods are not idempotent; it is an error to start or stop a stopwatch that is
|
||||||
|
* already in the desired state.
|
||||||
|
*
|
||||||
|
* <p>When testing code that uses this class, use {@link #createUnstarted(Ticker)} or {@link
|
||||||
|
* #createStarted(Ticker)} to supply a fake or mock ticker. This allows you to simulate any valid
|
||||||
|
* behavior of the stopwatch.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> This class is not thread-safe.
|
||||||
|
*
|
||||||
|
* <p><b>Warning for Android users:</b> a stopwatch with default behavior may not continue to keep
|
||||||
|
* time while the device is asleep. Instead, create one like this:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Stopwatch.createStarted(
|
||||||
|
* new Ticker() {
|
||||||
|
* public long read() {
|
||||||
|
* return android.os.SystemClock.elapsedRealtimeNanos();
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
@SuppressWarnings("GoodTime") // lots of violations
|
||||||
|
public final class Stopwatch {
|
||||||
|
private final Ticker ticker;
|
||||||
|
private boolean isRunning;
|
||||||
|
private long elapsedNanos;
|
||||||
|
private long startTick;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates (but does not start) a new stopwatch using {@link System#nanoTime} as its time source.
|
||||||
|
*
|
||||||
|
* @since 15.0
|
||||||
|
*/
|
||||||
|
public static Stopwatch createUnstarted() {
|
||||||
|
return new Stopwatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates (but does not start) a new stopwatch, using the specified time source.
|
||||||
|
*
|
||||||
|
* @since 15.0
|
||||||
|
*/
|
||||||
|
public static Stopwatch createUnstarted(Ticker ticker) {
|
||||||
|
return new Stopwatch(ticker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates (and starts) a new stopwatch using {@link System#nanoTime} as its time source.
|
||||||
|
*
|
||||||
|
* @since 15.0
|
||||||
|
*/
|
||||||
|
public static Stopwatch createStarted() {
|
||||||
|
return new Stopwatch().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates (and starts) a new stopwatch, using the specified time source.
|
||||||
|
*
|
||||||
|
* @since 15.0
|
||||||
|
*/
|
||||||
|
public static Stopwatch createStarted(Ticker ticker) {
|
||||||
|
return new Stopwatch(ticker).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stopwatch() {
|
||||||
|
this.ticker = Ticker.systemTicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stopwatch(Ticker ticker) {
|
||||||
|
this.ticker = checkNotNull(ticker, "ticker");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if {@link #start()} has been called on this stopwatch, and {@link #stop()}
|
||||||
|
* has not been called since the last call to {@code start()}.
|
||||||
|
*/
|
||||||
|
public boolean isRunning() {
|
||||||
|
return isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the stopwatch.
|
||||||
|
*
|
||||||
|
* @return this {@code Stopwatch} instance
|
||||||
|
* @throws IllegalStateException if the stopwatch is already running.
|
||||||
|
*/
|
||||||
|
public Stopwatch start() {
|
||||||
|
checkState(!isRunning, "This stopwatch is already running.");
|
||||||
|
isRunning = true;
|
||||||
|
startTick = ticker.read();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the stopwatch. Future reads will return the fixed duration that had elapsed up to this
|
||||||
|
* point.
|
||||||
|
*
|
||||||
|
* @return this {@code Stopwatch} instance
|
||||||
|
* @throws IllegalStateException if the stopwatch is already stopped.
|
||||||
|
*/
|
||||||
|
public Stopwatch stop() {
|
||||||
|
long tick = ticker.read();
|
||||||
|
checkState(isRunning, "This stopwatch is already stopped.");
|
||||||
|
isRunning = false;
|
||||||
|
elapsedNanos += tick - startTick;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the elapsed time for this stopwatch to zero, and places it in a stopped state.
|
||||||
|
*
|
||||||
|
* @return this {@code Stopwatch} instance
|
||||||
|
*/
|
||||||
|
public Stopwatch reset() {
|
||||||
|
elapsedNanos = 0;
|
||||||
|
isRunning = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long elapsedNanos() {
|
||||||
|
return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current elapsed time shown on this stopwatch, expressed in the desired time unit,
|
||||||
|
* with any fraction rounded down.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> the overhead of measurement can be more than a microsecond, so it is generally
|
||||||
|
* not useful to specify {@link TimeUnit#NANOSECONDS} precision here.
|
||||||
|
*
|
||||||
|
* <p>It is generally not a good idea to use an ambiguous, unitless {@code long} to represent
|
||||||
|
* elapsed time. Therefore, we recommend using {@link #elapsed()} instead, which returns a
|
||||||
|
* strongly-typed {@link Duration} instance.
|
||||||
|
*
|
||||||
|
* @since 14.0 (since 10.0 as {@code elapsedTime()})
|
||||||
|
*/
|
||||||
|
public long elapsed(TimeUnit desiredUnit) {
|
||||||
|
return desiredUnit.convert(elapsedNanos(), NANOSECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current elapsed time shown on this stopwatch as a {@link Duration}. Unlike {@link
|
||||||
|
* #elapsed(TimeUnit)}, this method does not lose any precision due to rounding.
|
||||||
|
*
|
||||||
|
* @since 22.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public Duration elapsed() {
|
||||||
|
return Duration.ofNanos(elapsedNanos());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a string representation of the current elapsed time. */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
long nanos = elapsedNanos();
|
||||||
|
|
||||||
|
TimeUnit unit = chooseUnit(nanos);
|
||||||
|
double value = (double) nanos / NANOSECONDS.convert(1, unit);
|
||||||
|
|
||||||
|
// Too bad this functionality is not exposed as a regular method call
|
||||||
|
return Platform.formatCompact4Digits(value) + " " + abbreviate(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TimeUnit chooseUnit(long nanos) {
|
||||||
|
if (DAYS.convert(nanos, NANOSECONDS) > 0) {
|
||||||
|
return DAYS;
|
||||||
|
}
|
||||||
|
if (HOURS.convert(nanos, NANOSECONDS) > 0) {
|
||||||
|
return HOURS;
|
||||||
|
}
|
||||||
|
if (MINUTES.convert(nanos, NANOSECONDS) > 0) {
|
||||||
|
return MINUTES;
|
||||||
|
}
|
||||||
|
if (SECONDS.convert(nanos, NANOSECONDS) > 0) {
|
||||||
|
return SECONDS;
|
||||||
|
}
|
||||||
|
if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
|
||||||
|
return MILLISECONDS;
|
||||||
|
}
|
||||||
|
if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
|
||||||
|
return MICROSECONDS;
|
||||||
|
}
|
||||||
|
return NANOSECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String abbreviate(TimeUnit unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case NANOSECONDS:
|
||||||
|
return "ns";
|
||||||
|
case MICROSECONDS:
|
||||||
|
return "\u03bcs"; // μs
|
||||||
|
case MILLISECONDS:
|
||||||
|
return "ms";
|
||||||
|
case SECONDS:
|
||||||
|
return "s";
|
||||||
|
case MINUTES:
|
||||||
|
return "min";
|
||||||
|
case HOURS:
|
||||||
|
return "h";
|
||||||
|
case DAYS:
|
||||||
|
return "d";
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
311
src/main/java/com/google/common/base/Strings.java
Normal file
311
src/main/java/com/google/common/base/Strings.java
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods pertaining to {@code String} or {@code CharSequence} instances.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class Strings {
|
||||||
|
private Strings() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given string if it is non-null; the empty string otherwise.
|
||||||
|
*
|
||||||
|
* @param string the string to test and possibly return
|
||||||
|
* @return {@code string} itself if it is non-null; {@code ""} if it is null
|
||||||
|
*/
|
||||||
|
public static String nullToEmpty(String string) {
|
||||||
|
return Platform.nullToEmpty(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given string if it is nonempty; {@code null} otherwise.
|
||||||
|
*
|
||||||
|
* @param string the string to test and possibly return
|
||||||
|
* @return {@code string} itself if it is nonempty; {@code null} if it is empty or null
|
||||||
|
*/
|
||||||
|
public static String emptyToNull(String string) {
|
||||||
|
return Platform.emptyToNull(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the given string is null or is the empty string.
|
||||||
|
*
|
||||||
|
* <p>Consider normalizing your string references with {@link #nullToEmpty}. If you do, you can
|
||||||
|
* use {@link String#isEmpty()} instead of this method, and you won't need special null-safe forms
|
||||||
|
* of methods like {@link String#toUpperCase} either. Or, if you'd like to normalize "in the other
|
||||||
|
* direction," converting empty strings to {@code null}, you can use {@link #emptyToNull}.
|
||||||
|
*
|
||||||
|
* @param string a string reference to check
|
||||||
|
* @return {@code true} if the string is null or is the empty string
|
||||||
|
*/
|
||||||
|
public static boolean isNullOrEmpty(String string) {
|
||||||
|
return Platform.stringIsNullOrEmpty(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string, of length at least {@code minLength}, consisting of {@code string} prepended
|
||||||
|
* with as many copies of {@code padChar} as are necessary to reach that length. For example,
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code padStart("7", 3, '0')} returns {@code "007"}
|
||||||
|
* <li>{@code padStart("2010", 3, '0')} returns {@code "2010"}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>See {@link java.util.Formatter} for a richer set of formatting capabilities.
|
||||||
|
*
|
||||||
|
* @param string the string which should appear at the end of the result
|
||||||
|
* @param minLength the minimum length the resulting string must have. Can be zero or negative, in
|
||||||
|
* which case the input string is always returned.
|
||||||
|
* @param padChar the character to insert at the beginning of the result until the minimum length
|
||||||
|
* is reached
|
||||||
|
* @return the padded string
|
||||||
|
*/
|
||||||
|
public static String padStart(String string, int minLength, char padChar) {
|
||||||
|
checkNotNull(string); // eager for GWT.
|
||||||
|
if (string.length() >= minLength) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder(minLength);
|
||||||
|
for (int i = string.length(); i < minLength; i++) {
|
||||||
|
sb.append(padChar);
|
||||||
|
}
|
||||||
|
sb.append(string);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string, of length at least {@code minLength}, consisting of {@code string} appended
|
||||||
|
* with as many copies of {@code padChar} as are necessary to reach that length. For example,
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code padEnd("4.", 5, '0')} returns {@code "4.000"}
|
||||||
|
* <li>{@code padEnd("2010", 3, '!')} returns {@code "2010"}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>See {@link java.util.Formatter} for a richer set of formatting capabilities.
|
||||||
|
*
|
||||||
|
* @param string the string which should appear at the beginning of the result
|
||||||
|
* @param minLength the minimum length the resulting string must have. Can be zero or negative, in
|
||||||
|
* which case the input string is always returned.
|
||||||
|
* @param padChar the character to append to the end of the result until the minimum length is
|
||||||
|
* reached
|
||||||
|
* @return the padded string
|
||||||
|
*/
|
||||||
|
public static String padEnd(String string, int minLength, char padChar) {
|
||||||
|
checkNotNull(string); // eager for GWT.
|
||||||
|
if (string.length() >= minLength) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder(minLength);
|
||||||
|
sb.append(string);
|
||||||
|
for (int i = string.length(); i < minLength; i++) {
|
||||||
|
sb.append(padChar);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string consisting of a specific number of concatenated copies of an input string. For
|
||||||
|
* example, {@code repeat("hey", 3)} returns the string {@code "heyheyhey"}.
|
||||||
|
*
|
||||||
|
* @param string any non-null string
|
||||||
|
* @param count the number of times to repeat it; a nonnegative integer
|
||||||
|
* @return a string containing {@code string} repeated {@code count} times (the empty string if
|
||||||
|
* {@code count} is zero)
|
||||||
|
* @throws IllegalArgumentException if {@code count} is negative
|
||||||
|
*/
|
||||||
|
public static String repeat(String string, int count) {
|
||||||
|
checkNotNull(string); // eager for GWT.
|
||||||
|
|
||||||
|
if (count <= 1) {
|
||||||
|
checkArgument(count >= 0, "invalid count: %s", count);
|
||||||
|
return (count == 0) ? "" : string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark
|
||||||
|
final int len = string.length();
|
||||||
|
final long longSize = (long) len * (long) count;
|
||||||
|
final int size = (int) longSize;
|
||||||
|
if (size != longSize) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
final char[] array = new char[size];
|
||||||
|
string.getChars(0, len, array, 0);
|
||||||
|
int n;
|
||||||
|
for (n = len; n < size - n; n <<= 1) {
|
||||||
|
System.arraycopy(array, 0, array, n, n);
|
||||||
|
}
|
||||||
|
System.arraycopy(array, 0, array, n, size - n);
|
||||||
|
return new String(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the longest string {@code prefix} such that {@code a.toString().startsWith(prefix) &&
|
||||||
|
* b.toString().startsWith(prefix)}, taking care not to split surrogate pairs. If {@code a} and
|
||||||
|
* {@code b} have no common prefix, returns the empty string.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public static String commonPrefix(CharSequence a, CharSequence b) {
|
||||||
|
checkNotNull(a);
|
||||||
|
checkNotNull(b);
|
||||||
|
|
||||||
|
int maxPrefixLength = Math.min(a.length(), b.length());
|
||||||
|
int p = 0;
|
||||||
|
while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) {
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
return a.subSequence(0, p).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the longest string {@code suffix} such that {@code a.toString().endsWith(suffix) &&
|
||||||
|
* b.toString().endsWith(suffix)}, taking care not to split surrogate pairs. If {@code a} and
|
||||||
|
* {@code b} have no common suffix, returns the empty string.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public static String commonSuffix(CharSequence a, CharSequence b) {
|
||||||
|
checkNotNull(a);
|
||||||
|
checkNotNull(b);
|
||||||
|
|
||||||
|
int maxSuffixLength = Math.min(a.length(), b.length());
|
||||||
|
int s = 0;
|
||||||
|
while (s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
if (validSurrogatePairAt(a, a.length() - s - 1)
|
||||||
|
|| validSurrogatePairAt(b, b.length() - s - 1)) {
|
||||||
|
s--;
|
||||||
|
}
|
||||||
|
return a.subSequence(a.length() - s, a.length()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when a valid surrogate pair starts at the given {@code index} in the given {@code string}.
|
||||||
|
* Out-of-range indexes return false.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
static boolean validSurrogatePairAt(CharSequence string, int index) {
|
||||||
|
return index >= 0
|
||||||
|
&& index <= (string.length() - 2)
|
||||||
|
&& Character.isHighSurrogate(string.charAt(index))
|
||||||
|
&& Character.isLowSurrogate(string.charAt(index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the given {@code template} string with each occurrence of {@code "%s"} replaced with
|
||||||
|
* the corresponding argument value from {@code args}; or, if the placeholder and argument counts
|
||||||
|
* do not match, returns a best-effort form of that string. Will not throw an exception under
|
||||||
|
* normal conditions.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> For most string-formatting needs, use {@link String#format String.format},
|
||||||
|
* {@link java.io.PrintWriter#format PrintWriter.format}, and related methods. These support the
|
||||||
|
* full range of <a
|
||||||
|
* href="https://docs.oracle.com/javase/9/docs/api/java/util/Formatter.html#syntax">format
|
||||||
|
* specifiers</a>, and alert you to usage errors by throwing {@link
|
||||||
|
* java.util.IllegalFormatException}.
|
||||||
|
*
|
||||||
|
* <p>In certain cases, such as outputting debugging information or constructing a message to be
|
||||||
|
* used for another unchecked exception, an exception during string formatting would serve little
|
||||||
|
* purpose except to supplant the real information you were trying to provide. These are the cases
|
||||||
|
* this method is made for; it instead generates a best-effort string with all supplied argument
|
||||||
|
* values present. This method is also useful in environments such as GWT where {@code
|
||||||
|
* String.format} is not available. As an example, method implementations of the {@link
|
||||||
|
* Preconditions} class use this formatter, for both of the reasons just discussed.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> Only the exact two-character placeholder sequence {@code "%s"} is
|
||||||
|
* recognized.
|
||||||
|
*
|
||||||
|
* @param template a string containing zero or more {@code "%s"} placeholder sequences. {@code
|
||||||
|
* null} is treated as the four-character string {@code "null"}.
|
||||||
|
* @param args the arguments to be substituted into the message template. The first argument
|
||||||
|
* specified is substituted for the first occurrence of {@code "%s"} in the template, and so
|
||||||
|
* forth. A {@code null} argument is converted to the four-character string {@code "null"};
|
||||||
|
* non-null values are converted to strings using {@link Object#toString()}.
|
||||||
|
* @since 25.1
|
||||||
|
*/
|
||||||
|
// TODO(diamondm) consider using Arrays.toString() for array parameters
|
||||||
|
public static String lenientFormat(String template, Object ... args) {
|
||||||
|
template = String.valueOf(template); // null -> "null"
|
||||||
|
|
||||||
|
if (args == null) {
|
||||||
|
args = new Object[] {"(Object[])null"};
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
args[i] = lenientToString(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start substituting the arguments into the '%s' placeholders
|
||||||
|
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
|
||||||
|
int templateStart = 0;
|
||||||
|
int i = 0;
|
||||||
|
while (i < args.length) {
|
||||||
|
int placeholderStart = template.indexOf("%s", templateStart);
|
||||||
|
if (placeholderStart == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
builder.append(template, templateStart, placeholderStart);
|
||||||
|
builder.append(args[i++]);
|
||||||
|
templateStart = placeholderStart + 2;
|
||||||
|
}
|
||||||
|
builder.append(template, templateStart, template.length());
|
||||||
|
|
||||||
|
// if we run out of placeholders, append the extra args in square braces
|
||||||
|
if (i < args.length) {
|
||||||
|
builder.append(" [");
|
||||||
|
builder.append(args[i++]);
|
||||||
|
while (i < args.length) {
|
||||||
|
builder.append(", ");
|
||||||
|
builder.append(args[i++]);
|
||||||
|
}
|
||||||
|
builder.append(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String lenientToString(Object o) {
|
||||||
|
try {
|
||||||
|
return String.valueOf(o);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Default toString() behavior - see Object.toString()
|
||||||
|
String objectToString =
|
||||||
|
o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o));
|
||||||
|
// Logger is created inline with fixed name to avoid forcing Proguard to create another class.
|
||||||
|
Logger.getLogger("com.google.common.base.Strings")
|
||||||
|
.log(WARNING, "Exception during lenientFormat for " + objectToString, e);
|
||||||
|
return "<" + objectToString + " threw " + e.getClass().getName() + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
src/main/java/com/google/common/base/Supplier.java
Normal file
48
src/main/java/com/google/common/base/Supplier.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy version of {@link java.util.function.Supplier java.util.function.Supplier}. Semantically,
|
||||||
|
* this could be a factory, generator, builder, closure, or something else entirely. No guarantees
|
||||||
|
* are implied by this interface.
|
||||||
|
*
|
||||||
|
* <p>The {@link Suppliers} class provides common suppliers and related utilities.
|
||||||
|
*
|
||||||
|
* <p>As this interface extends {@code java.util.function.Supplier}, an instance of this type can be
|
||||||
|
* used as a {@code java.util.function.Supplier} directly. To use a {@code
|
||||||
|
* java.util.function.Supplier} in a context where a {@code com.google.common.base.Supplier} is
|
||||||
|
* needed, use {@code supplier::get}.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide article on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/FunctionalExplained">the use of {@code Function}</a>.
|
||||||
|
*
|
||||||
|
* @author Harry Heymann
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Supplier<T> extends java.util.function.Supplier<T> {
|
||||||
|
/**
|
||||||
|
* Retrieves an instance of the appropriate type. The returned object may or may not be a new
|
||||||
|
* instance, depending on the implementation.
|
||||||
|
*
|
||||||
|
* @return an instance of the appropriate type
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
T get();
|
||||||
|
}
|
364
src/main/java/com/google/common/base/Suppliers.java
Normal file
364
src/main/java/com/google/common/base/Suppliers.java
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful suppliers.
|
||||||
|
*
|
||||||
|
* <p>All methods return serializable suppliers as long as they're given serializable parameters.
|
||||||
|
*
|
||||||
|
* @author Laurence Gonsalves
|
||||||
|
* @author Harry Heymann
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class Suppliers {
|
||||||
|
private Suppliers() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new supplier which is the composition of the provided function and supplier. In other
|
||||||
|
* words, the new supplier's value will be computed by retrieving the value from {@code supplier},
|
||||||
|
* and then applying {@code function} to that value. Note that the resulting supplier will not
|
||||||
|
* call {@code supplier} or invoke {@code function} until it is called.
|
||||||
|
*/
|
||||||
|
public static <F, T> Supplier<T> compose(Function<? super F, T> function, Supplier<F> supplier) {
|
||||||
|
return new SupplierComposition<>(function, supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SupplierComposition<F, T> implements Supplier<T>, Serializable {
|
||||||
|
final Function<? super F, T> function;
|
||||||
|
final Supplier<F> supplier;
|
||||||
|
|
||||||
|
SupplierComposition(Function<? super F, T> function, Supplier<F> supplier) {
|
||||||
|
this.function = checkNotNull(function);
|
||||||
|
this.supplier = checkNotNull(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
return function.apply(supplier.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof SupplierComposition) {
|
||||||
|
SupplierComposition<?, ?> that = (SupplierComposition<?, ?>) obj;
|
||||||
|
return function.equals(that.function) && supplier.equals(that.supplier);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(function, supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Suppliers.compose(" + function + ", " + supplier + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
|
||||||
|
* and returns that value on subsequent calls to {@code get()}. See: <a
|
||||||
|
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||||
|
*
|
||||||
|
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
|
||||||
|
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
|
||||||
|
* form does not contain the cached value, which will be recalculated when {@code get()} is called
|
||||||
|
* on the reserialized instance.
|
||||||
|
*
|
||||||
|
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
|
||||||
|
* delegating calls until it returns valid data.
|
||||||
|
*
|
||||||
|
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
|
||||||
|
* returned directly.
|
||||||
|
*/
|
||||||
|
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
|
||||||
|
if (delegate instanceof NonSerializableMemoizingSupplier
|
||||||
|
|| delegate instanceof MemoizingSupplier) {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
return delegate instanceof Serializable
|
||||||
|
? new MemoizingSupplier<T>(delegate)
|
||||||
|
: new NonSerializableMemoizingSupplier<T>(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||||
|
final Supplier<T> delegate;
|
||||||
|
transient volatile boolean initialized;
|
||||||
|
// "value" does not need to be volatile; visibility piggy-backs
|
||||||
|
// on volatile read of "initialized".
|
||||||
|
transient T value;
|
||||||
|
|
||||||
|
MemoizingSupplier(Supplier<T> delegate) {
|
||||||
|
this.delegate = checkNotNull(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
// A 2-field variant of Double Checked Locking.
|
||||||
|
if (!initialized) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!initialized) {
|
||||||
|
T t = delegate.get();
|
||||||
|
value = t;
|
||||||
|
initialized = true;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Suppliers.memoize("
|
||||||
|
+ (initialized ? "<supplier that returned " + value + ">" : delegate)
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
||||||
|
volatile Supplier<T> delegate;
|
||||||
|
volatile boolean initialized;
|
||||||
|
// "value" does not need to be volatile; visibility piggy-backs
|
||||||
|
// on volatile read of "initialized".
|
||||||
|
T value;
|
||||||
|
|
||||||
|
NonSerializableMemoizingSupplier(Supplier<T> delegate) {
|
||||||
|
this.delegate = checkNotNull(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
// A 2-field variant of Double Checked Locking.
|
||||||
|
if (!initialized) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!initialized) {
|
||||||
|
T t = delegate.get();
|
||||||
|
value = t;
|
||||||
|
initialized = true;
|
||||||
|
// Release the delegate to GC.
|
||||||
|
delegate = null;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
Supplier<T> delegate = this.delegate;
|
||||||
|
return "Suppliers.memoize("
|
||||||
|
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a supplier that caches the instance supplied by the delegate and removes the cached
|
||||||
|
* value after the specified time has passed. Subsequent calls to {@code get()} return the cached
|
||||||
|
* value if the expiration time has not passed. After the expiration time, a new value is
|
||||||
|
* retrieved, cached, and returned. See: <a
|
||||||
|
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||||
|
*
|
||||||
|
* <p>The returned supplier is thread-safe. The supplier's serialized form does not contain the
|
||||||
|
* cached value, which will be recalculated when {@code get()} is called on the reserialized
|
||||||
|
* instance. The actual memoization does not happen when the underlying delegate throws an
|
||||||
|
* exception.
|
||||||
|
*
|
||||||
|
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
|
||||||
|
* delegating calls until it returns valid data.
|
||||||
|
*
|
||||||
|
* @param duration the length of time after a value is created that it should stop being returned
|
||||||
|
* by subsequent {@code get()} calls
|
||||||
|
* @param unit the unit that {@code duration} is expressed in
|
||||||
|
* @throws IllegalArgumentException if {@code duration} is not positive
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||||
|
public static <T> Supplier<T> memoizeWithExpiration(
|
||||||
|
Supplier<T> delegate, long duration, TimeUnit unit) {
|
||||||
|
return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@SuppressWarnings("GoodTime") // lots of violations
|
||||||
|
static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||||
|
final Supplier<T> delegate;
|
||||||
|
final long durationNanos;
|
||||||
|
transient volatile T value;
|
||||||
|
// The special value 0 means "not yet initialized".
|
||||||
|
transient volatile long expirationNanos;
|
||||||
|
|
||||||
|
ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
|
||||||
|
this.delegate = checkNotNull(delegate);
|
||||||
|
this.durationNanos = unit.toNanos(duration);
|
||||||
|
checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
// Another variant of Double Checked Locking.
|
||||||
|
//
|
||||||
|
// We use two volatile reads. We could reduce this to one by
|
||||||
|
// putting our fields into a holder class, but (at least on x86)
|
||||||
|
// the extra memory consumption and indirection are more
|
||||||
|
// expensive than the extra volatile reads.
|
||||||
|
long nanos = expirationNanos;
|
||||||
|
long now = Platform.systemNanoTime();
|
||||||
|
if (nanos == 0 || now - nanos >= 0) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (nanos == expirationNanos) { // recheck for lost race
|
||||||
|
T t = delegate.get();
|
||||||
|
value = t;
|
||||||
|
nanos = now + durationNanos;
|
||||||
|
// In the very unlikely event that nanos is 0, set it to 1;
|
||||||
|
// no one will notice 1 ns of tardiness.
|
||||||
|
expirationNanos = (nanos == 0) ? 1 : nanos;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// This is a little strange if the unit the user provided was not NANOS,
|
||||||
|
// but we don't want to store the unit just for toString
|
||||||
|
return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a supplier that always supplies {@code instance}. */
|
||||||
|
public static <T> Supplier<T> ofInstance(T instance) {
|
||||||
|
return new SupplierOfInstance<T>(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SupplierOfInstance<T> implements Supplier<T>, Serializable {
|
||||||
|
final T instance;
|
||||||
|
|
||||||
|
SupplierOfInstance(T instance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof SupplierOfInstance) {
|
||||||
|
SupplierOfInstance<?> that = (SupplierOfInstance<?>) obj;
|
||||||
|
return Objects.equal(instance, that.instance);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Suppliers.ofInstance(" + instance + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a supplier whose {@code get()} method synchronizes on {@code delegate} before calling
|
||||||
|
* it, making it thread-safe.
|
||||||
|
*/
|
||||||
|
public static <T> Supplier<T> synchronizedSupplier(Supplier<T> delegate) {
|
||||||
|
return new ThreadSafeSupplier<T>(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ThreadSafeSupplier<T> implements Supplier<T>, Serializable {
|
||||||
|
final Supplier<T> delegate;
|
||||||
|
|
||||||
|
ThreadSafeSupplier(Supplier<T> delegate) {
|
||||||
|
this.delegate = checkNotNull(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
synchronized (delegate) {
|
||||||
|
return delegate.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Suppliers.synchronizedSupplier(" + delegate + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that accepts a supplier and returns the result of invoking {@link
|
||||||
|
* Supplier#get} on that supplier.
|
||||||
|
*
|
||||||
|
* <p><b>Java 8 users:</b> use the method reference {@code Supplier::get} instead.
|
||||||
|
*
|
||||||
|
* @since 8.0
|
||||||
|
*/
|
||||||
|
public static <T> Function<Supplier<T>, T> supplierFunction() {
|
||||||
|
@SuppressWarnings("unchecked") // implementation is "fully variant"
|
||||||
|
SupplierFunction<T> sf = (SupplierFunction<T>) SupplierFunctionImpl.INSTANCE;
|
||||||
|
return sf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface SupplierFunction<T> extends Function<Supplier<T>, T> {}
|
||||||
|
|
||||||
|
private enum SupplierFunctionImpl implements SupplierFunction<Object> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
// Note: This makes T a "pass-through type"
|
||||||
|
@Override
|
||||||
|
public Object apply(Supplier<Object> input) {
|
||||||
|
return input.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Suppliers.supplierFunction()";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
536
src/main/java/com/google/common/base/Throwables.java
Normal file
536
src/main/java/com/google/common/base/Throwables.java
Normal file
|
@ -0,0 +1,536 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.unmodifiableList;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods pertaining to instances of {@link Throwable}.
|
||||||
|
*
|
||||||
|
* <p>See the Guava User Guide entry on <a
|
||||||
|
* href="https://github.com/google/guava/wiki/ThrowablesExplained">Throwables</a>.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @author Ben Yu
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public final class Throwables {
|
||||||
|
private Throwables() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws {@code throwable} if it is an instance of {@code declaredType}. Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* for (Foo foo : foos) {
|
||||||
|
* try {
|
||||||
|
* foo.bar();
|
||||||
|
* } catch (BarException | RuntimeException | Error t) {
|
||||||
|
* failure = t;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* if (failure != null) {
|
||||||
|
* throwIfInstanceOf(failure, BarException.class);
|
||||||
|
* throwIfUnchecked(failure);
|
||||||
|
* throw new AssertionError(failure);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @since 20.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Class.cast, Class.isInstance
|
||||||
|
public static <X extends Throwable> void throwIfInstanceOf(
|
||||||
|
Throwable throwable, Class<X> declaredType) throws X {
|
||||||
|
checkNotNull(throwable);
|
||||||
|
if (declaredType.isInstance(throwable)) {
|
||||||
|
throw declaredType.cast(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@code
|
||||||
|
* declaredType}. Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* try {
|
||||||
|
* someMethodThatCouldThrowAnything();
|
||||||
|
* } catch (IKnowWhatToDoWithThisException e) {
|
||||||
|
* handle(e);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* Throwables.propagateIfInstanceOf(t, IOException.class);
|
||||||
|
* Throwables.propagateIfInstanceOf(t, SQLException.class);
|
||||||
|
* throw Throwables.propagate(t);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #throwIfInstanceOf}, which has the same behavior but rejects {@code
|
||||||
|
* null}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@GwtIncompatible // throwIfInstanceOf
|
||||||
|
public static <X extends Throwable> void propagateIfInstanceOf(
|
||||||
|
Throwable throwable, Class<X> declaredType) throws X {
|
||||||
|
if (throwable != null) {
|
||||||
|
throwIfInstanceOf(throwable, declaredType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws {@code throwable} if it is a {@link RuntimeException} or {@link Error}. Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* for (Foo foo : foos) {
|
||||||
|
* try {
|
||||||
|
* foo.bar();
|
||||||
|
* } catch (RuntimeException | Error t) {
|
||||||
|
* failure = t;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* if (failure != null) {
|
||||||
|
* throwIfUnchecked(failure);
|
||||||
|
* throw new AssertionError(failure);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @since 20.0
|
||||||
|
*/
|
||||||
|
public static void throwIfUnchecked(Throwable throwable) {
|
||||||
|
checkNotNull(throwable);
|
||||||
|
if (throwable instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) throwable;
|
||||||
|
}
|
||||||
|
if (throwable instanceof Error) {
|
||||||
|
throw (Error) throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link
|
||||||
|
* RuntimeException} or {@link Error}. Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* try {
|
||||||
|
* someMethodThatCouldThrowAnything();
|
||||||
|
* } catch (IKnowWhatToDoWithThisException e) {
|
||||||
|
* handle(e);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* Throwables.propagateIfPossible(t);
|
||||||
|
* throw new RuntimeException("unexpected", t);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #throwIfUnchecked}, which has the same behavior but rejects {@code
|
||||||
|
* null}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@GwtIncompatible
|
||||||
|
public static void propagateIfPossible(Throwable throwable) {
|
||||||
|
if (throwable != null) {
|
||||||
|
throwIfUnchecked(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link
|
||||||
|
* RuntimeException}, {@link Error}, or {@code declaredType}. Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* try {
|
||||||
|
* someMethodThatCouldThrowAnything();
|
||||||
|
* } catch (IKnowWhatToDoWithThisException e) {
|
||||||
|
* handle(e);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* Throwables.propagateIfPossible(t, OtherException.class);
|
||||||
|
* throw new RuntimeException("unexpected", t);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param throwable the Throwable to possibly propagate
|
||||||
|
* @param declaredType the single checked exception type declared by the calling method
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // propagateIfInstanceOf
|
||||||
|
public static <X extends Throwable> void propagateIfPossible(
|
||||||
|
Throwable throwable, Class<X> declaredType) throws X {
|
||||||
|
propagateIfInstanceOf(throwable, declaredType);
|
||||||
|
propagateIfPossible(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link
|
||||||
|
* RuntimeException}, {@link Error}, {@code declaredType1}, or {@code declaredType2}. In the
|
||||||
|
* unlikely case that you have three or more declared checked exception types, you can handle them
|
||||||
|
* all by invoking these methods repeatedly. See usage example in {@link
|
||||||
|
* #propagateIfPossible(Throwable, Class)}.
|
||||||
|
*
|
||||||
|
* @param throwable the Throwable to possibly propagate
|
||||||
|
* @param declaredType1 any checked exception type declared by the calling method
|
||||||
|
* @param declaredType2 any other checked exception type declared by the calling method
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // propagateIfInstanceOf
|
||||||
|
public static <X1 extends Throwable, X2 extends Throwable> void propagateIfPossible(
|
||||||
|
Throwable throwable, Class<X1> declaredType1, Class<X2> declaredType2)
|
||||||
|
throws X1, X2 {
|
||||||
|
checkNotNull(declaredType2);
|
||||||
|
propagateIfInstanceOf(throwable, declaredType1);
|
||||||
|
propagateIfPossible(throwable, declaredType2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates {@code throwable} as-is if it is an instance of {@link RuntimeException} or {@link
|
||||||
|
* Error}, or else as a last resort, wraps it in a {@code RuntimeException} and then propagates.
|
||||||
|
*
|
||||||
|
* <p>This method always throws an exception. The {@code RuntimeException} return type allows
|
||||||
|
* client code to signal to the compiler that statements after the call are unreachable. Example
|
||||||
|
* usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* T doSomething() {
|
||||||
|
* try {
|
||||||
|
* return someMethodThatCouldThrowAnything();
|
||||||
|
* } catch (IKnowWhatToDoWithThisException e) {
|
||||||
|
* return handle(e);
|
||||||
|
* } catch (Throwable t) {
|
||||||
|
* throw Throwables.propagate(t);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param throwable the Throwable to propagate
|
||||||
|
* @return nothing will ever be returned; this return type is only for your convenience, as
|
||||||
|
* illustrated in the example above
|
||||||
|
* @deprecated Use {@code throw e} or {@code throw new RuntimeException(e)} directly, or use a
|
||||||
|
* combination of {@link #throwIfUnchecked} and {@code throw new RuntimeException(e)}. For
|
||||||
|
* background on the deprecation, read <a href="https://goo.gl/Ivn2kc">Why we deprecated
|
||||||
|
* {@code Throwables.propagate}</a>.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
@Deprecated
|
||||||
|
public static RuntimeException propagate(Throwable throwable) {
|
||||||
|
throwIfUnchecked(throwable);
|
||||||
|
throw new RuntimeException(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the innermost cause of {@code throwable}. The first throwable in a chain provides
|
||||||
|
* context from when the error or exception was initially detected. Example usage:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* assertEquals("Unable to assign a customer id", Throwables.getRootCause(e).getMessage());
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if there is a loop in the causal chain
|
||||||
|
*/
|
||||||
|
public static Throwable getRootCause(Throwable throwable) {
|
||||||
|
// Keep a second pointer that slowly walks the causal chain. If the fast pointer ever catches
|
||||||
|
// the slower pointer, then there's a loop.
|
||||||
|
Throwable slowPointer = throwable;
|
||||||
|
boolean advanceSlowPointer = false;
|
||||||
|
|
||||||
|
Throwable cause;
|
||||||
|
while ((cause = throwable.getCause()) != null) {
|
||||||
|
throwable = cause;
|
||||||
|
|
||||||
|
if (throwable == slowPointer) {
|
||||||
|
throw new IllegalArgumentException("Loop in causal chain detected.", throwable);
|
||||||
|
}
|
||||||
|
if (advanceSlowPointer) {
|
||||||
|
slowPointer = slowPointer.getCause();
|
||||||
|
}
|
||||||
|
advanceSlowPointer = !advanceSlowPointer; // only advance every other iteration
|
||||||
|
}
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a {@code Throwable} cause chain as a list. The first entry in the list will be {@code
|
||||||
|
* throwable} followed by its cause hierarchy. Note that this is a snapshot of the cause chain and
|
||||||
|
* will not reflect any subsequent changes to the cause chain.
|
||||||
|
*
|
||||||
|
* <p>Here's an example of how it can be used to find specific types of exceptions in the cause
|
||||||
|
* chain:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Iterables.filter(Throwables.getCausalChain(e), IOException.class));
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param throwable the non-null {@code Throwable} to extract causes from
|
||||||
|
* @return an unmodifiable list containing the cause chain starting with {@code throwable}
|
||||||
|
* @throws IllegalArgumentException if there is a loop in the causal chain
|
||||||
|
*/
|
||||||
|
@Beta // TODO(kevinb): decide best return type
|
||||||
|
public static List<Throwable> getCausalChain(Throwable throwable) {
|
||||||
|
checkNotNull(throwable);
|
||||||
|
List<Throwable> causes = new ArrayList<>(4);
|
||||||
|
causes.add(throwable);
|
||||||
|
|
||||||
|
// Keep a second pointer that slowly walks the causal chain. If the fast pointer ever catches
|
||||||
|
// the slower pointer, then there's a loop.
|
||||||
|
Throwable slowPointer = throwable;
|
||||||
|
boolean advanceSlowPointer = false;
|
||||||
|
|
||||||
|
Throwable cause;
|
||||||
|
while ((cause = throwable.getCause()) != null) {
|
||||||
|
throwable = cause;
|
||||||
|
causes.add(throwable);
|
||||||
|
|
||||||
|
if (throwable == slowPointer) {
|
||||||
|
throw new IllegalArgumentException("Loop in causal chain detected.", throwable);
|
||||||
|
}
|
||||||
|
if (advanceSlowPointer) {
|
||||||
|
slowPointer = slowPointer.getCause();
|
||||||
|
}
|
||||||
|
advanceSlowPointer = !advanceSlowPointer; // only advance every other iteration
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(causes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code throwable}'s cause, cast to {@code expectedCauseType}.
|
||||||
|
*
|
||||||
|
* <p>Prefer this method instead of manually casting an exception's cause. For example, {@code
|
||||||
|
* (IOException) e.getCause()} throws a {@link ClassCastException} that discards the original
|
||||||
|
* exception {@code e} if the cause is not an {@link IOException}, but {@code
|
||||||
|
* Throwables.getCauseAs(e, IOException.class)} keeps {@code e} as the {@link
|
||||||
|
* ClassCastException}'s cause.
|
||||||
|
*
|
||||||
|
* @throws ClassCastException if the cause cannot be cast to the expected type. The {@code
|
||||||
|
* ClassCastException}'s cause is {@code throwable}.
|
||||||
|
* @since 22.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
@GwtIncompatible // Class.cast(Object)
|
||||||
|
public static <X extends Throwable> X getCauseAs(
|
||||||
|
Throwable throwable, Class<X> expectedCauseType) {
|
||||||
|
try {
|
||||||
|
return expectedCauseType.cast(throwable.getCause());
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
e.initCause(throwable);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string containing the result of {@link Throwable#toString() toString()}, followed by
|
||||||
|
* the full, recursive stack trace of {@code throwable}. Note that you probably should not be
|
||||||
|
* parsing the resulting string; if you need programmatic access to the stack frames, you can call
|
||||||
|
* {@link Throwable#getStackTrace()}.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.io.PrintWriter, java.io.StringWriter
|
||||||
|
public static String getStackTraceAsString(Throwable throwable) {
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
throwable.printStackTrace(new PrintWriter(stringWriter));
|
||||||
|
return stringWriter.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stack trace of {@code throwable}, possibly providing slower iteration over the full
|
||||||
|
* trace but faster iteration over parts of the trace. Here, "slower" and "faster" are defined in
|
||||||
|
* comparison to the normal way to access the stack trace, {@link Throwable#getStackTrace()
|
||||||
|
* throwable.getStackTrace()}. Note, however, that this method's special implementation is not
|
||||||
|
* available for all platforms and configurations. If that implementation is unavailable, this
|
||||||
|
* method falls back to {@code getStackTrace}. Callers that require the special implementation can
|
||||||
|
* check its availability with {@link #lazyStackTraceIsLazy()}.
|
||||||
|
*
|
||||||
|
* <p>The expected (but not guaranteed) performance of the special implementation differs from
|
||||||
|
* {@code getStackTrace} in one main way: The {@code lazyStackTrace} call itself returns quickly
|
||||||
|
* by delaying the per-stack-frame work until each element is accessed. Roughly speaking:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code getStackTrace} takes {@code stackSize} time to return but then negligible time to
|
||||||
|
* retrieve each element of the returned list.
|
||||||
|
* <li>{@code lazyStackTrace} takes negligible time to return but then {@code 1/stackSize} time
|
||||||
|
* to retrieve each element of the returned list (probably slightly more than {@code
|
||||||
|
* 1/stackSize}).
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Note: The special implementation does not respect calls to {@link Throwable#setStackTrace
|
||||||
|
* throwable.setStackTrace}. Instead, it always reflects the original stack trace from the
|
||||||
|
* exception's creation.
|
||||||
|
*
|
||||||
|
* @since 19.0
|
||||||
|
*/
|
||||||
|
// TODO(cpovirk): Say something about the possibility that List access could fail at runtime?
|
||||||
|
@Beta
|
||||||
|
@GwtIncompatible // lazyStackTraceIsLazy, jlaStackTrace
|
||||||
|
// TODO(cpovirk): Consider making this available under GWT (slow implementation only).
|
||||||
|
public static List<StackTraceElement> lazyStackTrace(Throwable throwable) {
|
||||||
|
return lazyStackTraceIsLazy()
|
||||||
|
? jlaStackTrace(throwable)
|
||||||
|
: unmodifiableList(asList(throwable.getStackTrace()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether {@link #lazyStackTrace} will use the special implementation described in its
|
||||||
|
* documentation.
|
||||||
|
*
|
||||||
|
* @since 19.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
@GwtIncompatible // getStackTraceElementMethod
|
||||||
|
public static boolean lazyStackTraceIsLazy() {
|
||||||
|
return getStackTraceElementMethod != null && getStackTraceDepthMethod != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // invokeAccessibleNonThrowingMethod
|
||||||
|
private static List<StackTraceElement> jlaStackTrace(final Throwable t) {
|
||||||
|
checkNotNull(t);
|
||||||
|
/*
|
||||||
|
* TODO(cpovirk): Consider optimizing iterator() to catch IOOBE instead of doing bounds checks.
|
||||||
|
*
|
||||||
|
* TODO(cpovirk): Consider the UnsignedBytes pattern if it performs faster and doesn't cause
|
||||||
|
* AOSP grief.
|
||||||
|
*/
|
||||||
|
return new AbstractList<StackTraceElement>() {
|
||||||
|
@Override
|
||||||
|
public StackTraceElement get(int n) {
|
||||||
|
return (StackTraceElement)
|
||||||
|
invokeAccessibleNonThrowingMethod(getStackTraceElementMethod, jla, t, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return (Integer) invokeAccessibleNonThrowingMethod(getStackTraceDepthMethod, jla, t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static Object invokeAccessibleNonThrowingMethod(
|
||||||
|
Method method, Object receiver, Object... params) {
|
||||||
|
try {
|
||||||
|
return method.invoke(receiver, params);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw propagate(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JavaLangAccess class name to load using reflection */
|
||||||
|
@GwtIncompatible // not used by GWT emulation
|
||||||
|
private static final String JAVA_LANG_ACCESS_CLASSNAME = "sun.misc.JavaLangAccess";
|
||||||
|
|
||||||
|
/** SharedSecrets class name to load using reflection */
|
||||||
|
@GwtIncompatible // not used by GWT emulation
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String SHARED_SECRETS_CLASSNAME = "sun.misc.SharedSecrets";
|
||||||
|
|
||||||
|
/** Access to some fancy internal JVM internals. */
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static final Object jla = getJLA();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "getStackTraceElementMethod" method, only available on some JDKs so we use reflection to
|
||||||
|
* find it when available. When this is null, use the slow way.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static final Method getStackTraceElementMethod =
|
||||||
|
(jla == null) ? null : getGetMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "getStackTraceDepth" method, only available on some JDKs so we use reflection to find it
|
||||||
|
* when available. When this is null, use the slow way.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static final Method getStackTraceDepthMethod =
|
||||||
|
(jla == null) ? null : getSizeMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JavaLangAccess class that is present in all Sun JDKs. It is not allowed in
|
||||||
|
* AppEngine, and not present in non-Sun JDKs.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static Object getJLA() {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* We load sun.misc.* classes using reflection since Android doesn't support these classes and
|
||||||
|
* would result in compilation failure if we directly refer to these classes.
|
||||||
|
*/
|
||||||
|
Class<?> sharedSecrets = Class.forName(SHARED_SECRETS_CLASSNAME, false, null);
|
||||||
|
Method langAccess = sharedSecrets.getMethod("getJavaLangAccess");
|
||||||
|
return langAccess.invoke(null);
|
||||||
|
} catch (ThreadDeath death) {
|
||||||
|
throw death;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
/*
|
||||||
|
* This is not one of AppEngine's allowed classes, so even in Sun JDKs, this can fail with
|
||||||
|
* a NoClassDefFoundError. Other apps might deny access to sun.misc packages.
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Method that can be used to resolve an individual StackTraceElement, or null if that
|
||||||
|
* method cannot be found (it is only to be found in fairly recent JDKs).
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static Method getGetMethod() {
|
||||||
|
return getJlaMethod("getStackTraceElement", Throwable.class, int.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Method that can be used to return the size of a stack, or null if that method
|
||||||
|
* cannot be found (it is only to be found in fairly recent JDKs).
|
||||||
|
*
|
||||||
|
* <p>See <a href="https://github.com/google/guava/issues/2887">Throwables#lazyStackTrace throws
|
||||||
|
* UnsupportedOperationException</a>.
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static Method getSizeMethod() {
|
||||||
|
try {
|
||||||
|
Method getStackTraceDepth = getJlaMethod("getStackTraceDepth", Throwable.class);
|
||||||
|
if (getStackTraceDepth == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
getStackTraceDepth.invoke(getJLA(), new Throwable());
|
||||||
|
return getStackTraceDepth;
|
||||||
|
} catch (UnsupportedOperationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // java.lang.reflect
|
||||||
|
private static Method getJlaMethod(String name, Class<?>... parameterTypes)
|
||||||
|
throws ThreadDeath {
|
||||||
|
try {
|
||||||
|
return Class.forName(JAVA_LANG_ACCESS_CLASSNAME, false, null).getMethod(name, parameterTypes);
|
||||||
|
} catch (ThreadDeath death) {
|
||||||
|
throw death;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
/*
|
||||||
|
* Either the JavaLangAccess class itself is not found, or the method is not supported on the
|
||||||
|
* JVM.
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
src/main/java/com/google/common/base/Ticker.java
Normal file
54
src/main/java/com/google/common/base/Ticker.java
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A time source; returns a time value representing the number of nanoseconds elapsed since some
|
||||||
|
* fixed but arbitrary point in time. Note that most users should use {@link Stopwatch} instead of
|
||||||
|
* interacting with this class directly.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> this interface can only be used to measure elapsed time, not wall time.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 10.0 (<a href="https://github.com/google/guava/wiki/Compatibility">mostly
|
||||||
|
* source-compatible</a> since 9.0)
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public abstract class Ticker {
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected Ticker() {}
|
||||||
|
|
||||||
|
/** Returns the number of nanoseconds elapsed since this ticker's fixed point of reference. */
|
||||||
|
public abstract long read();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ticker that reads the current time using {@link System#nanoTime}.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public static Ticker systemTicker() {
|
||||||
|
return SYSTEM_TICKER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Ticker SYSTEM_TICKER =
|
||||||
|
new Ticker() {
|
||||||
|
@Override
|
||||||
|
public long read() {
|
||||||
|
return Platform.systemNanoTime();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
201
src/main/java/com/google/common/base/Utf8.java
Normal file
201
src/main/java/com/google/common/base/Utf8.java
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkPositionIndexes;
|
||||||
|
import static java.lang.Character.MAX_SURROGATE;
|
||||||
|
import static java.lang.Character.MIN_SURROGATE;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low-level, high-performance utility methods related to the {@linkplain Charsets#UTF_8 UTF-8}
|
||||||
|
* character encoding. UTF-8 is defined in section D92 of <a
|
||||||
|
* href="http://www.unicode.org/versions/Unicode6.2.0/ch03.pdf">The Unicode Standard Core
|
||||||
|
* Specification, Chapter 3</a>.
|
||||||
|
*
|
||||||
|
* <p>The variant of UTF-8 implemented by this class is the restricted definition of UTF-8
|
||||||
|
* introduced in Unicode 3.1. One implication of this is that it rejects <a
|
||||||
|
* href="http://www.unicode.org/versions/corrigendum1.html">"non-shortest form"</a> byte sequences,
|
||||||
|
* even though the JDK decoder may accept them.
|
||||||
|
*
|
||||||
|
* @author Martin Buchholz
|
||||||
|
* @author Clément Roux
|
||||||
|
* @since 16.0
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public final class Utf8 {
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string, this
|
||||||
|
* method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in both
|
||||||
|
* time and space.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
|
||||||
|
* surrogates)
|
||||||
|
*/
|
||||||
|
public static int encodedLength(CharSequence sequence) {
|
||||||
|
// Warning to maintainers: this implementation is highly optimized.
|
||||||
|
int utf16Length = sequence.length();
|
||||||
|
int utf8Length = utf16Length;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// This loop optimizes for pure ASCII.
|
||||||
|
while (i < utf16Length && sequence.charAt(i) < 0x80) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This loop optimizes for chars less than 0x800.
|
||||||
|
for (; i < utf16Length; i++) {
|
||||||
|
char c = sequence.charAt(i);
|
||||||
|
if (c < 0x800) {
|
||||||
|
utf8Length += ((0x7f - c) >>> 31); // branch free!
|
||||||
|
} else {
|
||||||
|
utf8Length += encodedLengthGeneral(sequence, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utf8Length < utf16Length) {
|
||||||
|
// Necessary and sufficient condition for overflow because of maximum 3x expansion
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"UTF-8 length does not fit in int: " + (utf8Length + (1L << 32)));
|
||||||
|
}
|
||||||
|
return utf8Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int encodedLengthGeneral(CharSequence sequence, int start) {
|
||||||
|
int utf16Length = sequence.length();
|
||||||
|
int utf8Length = 0;
|
||||||
|
for (int i = start; i < utf16Length; i++) {
|
||||||
|
char c = sequence.charAt(i);
|
||||||
|
if (c < 0x800) {
|
||||||
|
utf8Length += (0x7f - c) >>> 31; // branch free!
|
||||||
|
} else {
|
||||||
|
utf8Length += 2;
|
||||||
|
// jdk7+: if (Character.isSurrogate(c)) {
|
||||||
|
if (MIN_SURROGATE <= c && c <= MAX_SURROGATE) {
|
||||||
|
// Check that we have a well-formed surrogate pair.
|
||||||
|
if (Character.codePointAt(sequence, i) == c) {
|
||||||
|
throw new IllegalArgumentException(unpairedSurrogateMsg(i));
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return utf8Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if {@code bytes} is a <i>well-formed</i> UTF-8 byte sequence according to
|
||||||
|
* Unicode 6.0. Note that this is a stronger criterion than simply whether the bytes can be
|
||||||
|
* decoded. For example, some versions of the JDK decoder will accept "non-shortest form" byte
|
||||||
|
* sequences, but encoding never reproduces these. Such byte sequences are <i>not</i> considered
|
||||||
|
* well-formed.
|
||||||
|
*
|
||||||
|
* <p>This method returns {@code true} if and only if {@code Arrays.equals(bytes, new
|
||||||
|
* String(bytes, UTF_8).getBytes(UTF_8))} does, but is more efficient in both time and space.
|
||||||
|
*/
|
||||||
|
public static boolean isWellFormed(byte[] bytes) {
|
||||||
|
return isWellFormed(bytes, 0, bytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given byte array slice is a well-formed UTF-8 byte sequence, as defined by
|
||||||
|
* {@link #isWellFormed(byte[])}. Note that this can be false even when {@code
|
||||||
|
* isWellFormed(bytes)} is true.
|
||||||
|
*
|
||||||
|
* @param bytes the input buffer
|
||||||
|
* @param off the offset in the buffer of the first byte to read
|
||||||
|
* @param len the number of bytes to read from the buffer
|
||||||
|
*/
|
||||||
|
public static boolean isWellFormed(byte[] bytes, int off, int len) {
|
||||||
|
int end = off + len;
|
||||||
|
checkPositionIndexes(off, end, bytes.length);
|
||||||
|
// Look for the first non-ASCII character.
|
||||||
|
for (int i = off; i < end; i++) {
|
||||||
|
if (bytes[i] < 0) {
|
||||||
|
return isWellFormedSlowPath(bytes, i, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWellFormedSlowPath(byte[] bytes, int off, int end) {
|
||||||
|
int index = off;
|
||||||
|
while (true) {
|
||||||
|
int byte1;
|
||||||
|
|
||||||
|
// Optimize for interior runs of ASCII bytes.
|
||||||
|
do {
|
||||||
|
if (index >= end) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} while ((byte1 = bytes[index++]) >= 0);
|
||||||
|
|
||||||
|
if (byte1 < (byte) 0xE0) {
|
||||||
|
// Two-byte form.
|
||||||
|
if (index == end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Simultaneously check for illegal trailing-byte in leading position
|
||||||
|
// and overlong 2-byte form.
|
||||||
|
if (byte1 < (byte) 0xC2 || bytes[index++] > (byte) 0xBF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (byte1 < (byte) 0xF0) {
|
||||||
|
// Three-byte form.
|
||||||
|
if (index + 1 >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int byte2 = bytes[index++];
|
||||||
|
if (byte2 > (byte) 0xBF
|
||||||
|
// Overlong? 5 most significant bits must not all be zero.
|
||||||
|
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
|
||||||
|
// Check for illegal surrogate codepoints.
|
||||||
|
|| (byte1 == (byte) 0xED && (byte) 0xA0 <= byte2)
|
||||||
|
// Third byte trailing-byte test.
|
||||||
|
|| bytes[index++] > (byte) 0xBF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Four-byte form.
|
||||||
|
if (index + 2 >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int byte2 = bytes[index++];
|
||||||
|
if (byte2 > (byte) 0xBF
|
||||||
|
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||||
|
// if (byte1 > (byte) 0xF4
|
||||||
|
// || byte1 == (byte) 0xF0 && byte2 < (byte) 0x90
|
||||||
|
// || byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||||
|
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
|
||||||
|
// Third byte trailing-byte test
|
||||||
|
|| bytes[index++] > (byte) 0xBF
|
||||||
|
// Fourth byte trailing-byte test
|
||||||
|
|| bytes[index++] > (byte) 0xBF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unpairedSurrogateMsg(int i) {
|
||||||
|
return "Unpaired surrogate at index " + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Utf8() {}
|
||||||
|
}
|
504
src/main/java/com/google/common/base/Verify.java
Normal file
504
src/main/java/com/google/common/base/Verify.java
Normal file
|
@ -0,0 +1,504 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.lenientFormat;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static convenience methods that serve the same purpose as Java language <a
|
||||||
|
* href="https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html">assertions</a>,
|
||||||
|
* except that they are always enabled. These methods should be used instead of Java assertions
|
||||||
|
* whenever there is a chance the check may fail "in real life". Example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Bill bill = remoteService.getLastUnpaidBill();
|
||||||
|
*
|
||||||
|
* // In case bug 12345 happens again we'd rather just die
|
||||||
|
* Verify.verify(bill.status() == Status.UNPAID,
|
||||||
|
* "Unexpected bill status: %s", bill.status());
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <h3>Comparison to alternatives</h3>
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> In some cases the differences explained below can be subtle. When it's unclear
|
||||||
|
* which approach to use, <b>don't worry</b> too much about it; just pick something that seems
|
||||||
|
* reasonable and it will be fine.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>If checking whether the <i>caller</i> has violated your method or constructor's contract
|
||||||
|
* (such as by passing an invalid argument), use the utilities of the {@link Preconditions}
|
||||||
|
* class instead.
|
||||||
|
* <li>If checking an <i>impossible</i> condition (which <i>cannot</i> happen unless your own
|
||||||
|
* class or its <i>trusted</i> dependencies is badly broken), this is what ordinary Java
|
||||||
|
* assertions are for. Note that assertions are not enabled by default; they are essentially
|
||||||
|
* considered "compiled comments."
|
||||||
|
* <li>An explicit {@code if/throw} (as illustrated below) is always acceptable; we still
|
||||||
|
* recommend using our {@link VerifyException} exception type. Throwing a plain {@link
|
||||||
|
* RuntimeException} is frowned upon.
|
||||||
|
* <li>Use of {@link java.util.Objects#requireNonNull(Object)} is generally discouraged, since
|
||||||
|
* {@link #verifyNotNull(Object)} and {@link Preconditions#checkNotNull(Object)} perform the
|
||||||
|
* same function with more clarity.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h3>Warning about performance</h3>
|
||||||
|
*
|
||||||
|
* <p>Remember that parameter values for message construction must all be computed eagerly, and
|
||||||
|
* autoboxing and varargs array creation may happen as well, even when the verification succeeds and
|
||||||
|
* the message ends up unneeded. Performance-sensitive verification checks should continue to use
|
||||||
|
* usual form:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Bill bill = remoteService.getLastUnpaidBill();
|
||||||
|
* if (bill.status() != Status.UNPAID) {
|
||||||
|
* throw new VerifyException("Unexpected bill status: " + bill.status());
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <h3>Only {@code %s} is supported</h3>
|
||||||
|
*
|
||||||
|
* <p>As with {@link Preconditions}, {@code Verify} uses {@link Strings#lenientFormat} to format
|
||||||
|
* error message template strings. This only supports the {@code "%s"} specifier, not the full range
|
||||||
|
* of {@link java.util.Formatter} specifiers. However, note that if the number of arguments does not
|
||||||
|
* match the number of occurrences of {@code "%s"} in the format string, {@code Verify} will still
|
||||||
|
* behave as expected, and will still include all argument values in the error message; the message
|
||||||
|
* will simply not be formatted exactly as intended.
|
||||||
|
*
|
||||||
|
* <h3>More information</h3>
|
||||||
|
*
|
||||||
|
* See <a href="https://github.com/google/guava/wiki/ConditionalFailuresExplained">Conditional
|
||||||
|
* failures explained</a> in the Guava User Guide for advice on when this class should be used.
|
||||||
|
*
|
||||||
|
* @since 17.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class Verify {
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with no
|
||||||
|
* message otherwise.
|
||||||
|
*
|
||||||
|
* @throws VerifyException if {@code expression} is {@code false}
|
||||||
|
* @see Preconditions#checkState Preconditions.checkState()
|
||||||
|
*/
|
||||||
|
public static void verify(boolean expression) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* @param expression a boolean expression
|
||||||
|
* @param errorMessageTemplate a template for the exception message should the check fail. The
|
||||||
|
* message is formed by replacing each {@code %s} placeholder in the template with an
|
||||||
|
* argument. These are matched by position - the first {@code %s} gets {@code
|
||||||
|
* errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in
|
||||||
|
* square braces. Unmatched placeholders will be left as-is.
|
||||||
|
* @param errorMessageArgs the arguments to be substituted into the message template. Arguments
|
||||||
|
* are converted to strings using {@link String#valueOf(Object)}.
|
||||||
|
* @throws VerifyException if {@code expression} is {@code false}
|
||||||
|
* @see Preconditions#checkState Preconditions.checkState()
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression,
|
||||||
|
String errorMessageTemplate,
|
||||||
|
Object ... errorMessageArgs) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, errorMessageArgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(boolean expression, String errorMessageTemplate, char p1) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(boolean expression, String errorMessageTemplate, int p1) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(boolean expression, String errorMessageTemplate, long p1) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, Object p1) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, char p1, char p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, int p1, char p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, long p1, char p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, Object p1, char p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, char p1, int p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, int p1, int p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, long p1, int p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, Object p1, int p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, char p1, long p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, int p1, long p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, long p1, long p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, Object p1, long p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, char p1, Object p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, int p1, Object p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression, String errorMessageTemplate, long p1, Object p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression,
|
||||||
|
String errorMessageTemplate,
|
||||||
|
Object p1,
|
||||||
|
Object p2) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression,
|
||||||
|
String errorMessageTemplate,
|
||||||
|
Object p1,
|
||||||
|
Object p2,
|
||||||
|
Object p3) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2, p3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a
|
||||||
|
* custom message otherwise.
|
||||||
|
*
|
||||||
|
* <p>See {@link #verify(boolean, String, Object...)} for details.
|
||||||
|
*
|
||||||
|
* @since 23.1 (varargs overload since 17.0)
|
||||||
|
*/
|
||||||
|
public static void verify(
|
||||||
|
boolean expression,
|
||||||
|
String errorMessageTemplate,
|
||||||
|
Object p1,
|
||||||
|
Object p2,
|
||||||
|
Object p3,
|
||||||
|
Object p4) {
|
||||||
|
if (!expression) {
|
||||||
|
throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code reference} is non-null, throwing a {@code VerifyException} with a default
|
||||||
|
* message otherwise.
|
||||||
|
*
|
||||||
|
* @return {@code reference}, guaranteed to be non-null, for convenience
|
||||||
|
* @throws VerifyException if {@code reference} is {@code null}
|
||||||
|
* @see Preconditions#checkNotNull Preconditions.checkNotNull()
|
||||||
|
*/
|
||||||
|
public static <T> T verifyNotNull(T reference) {
|
||||||
|
return verifyNotNull(reference, "expected a non-null reference");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that {@code reference} is non-null, throwing a {@code VerifyException} with a custom
|
||||||
|
* message otherwise.
|
||||||
|
*
|
||||||
|
* @param errorMessageTemplate a template for the exception message should the check fail. The
|
||||||
|
* message is formed by replacing each {@code %s} placeholder in the template with an
|
||||||
|
* argument. These are matched by position - the first {@code %s} gets {@code
|
||||||
|
* errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in
|
||||||
|
* square braces. Unmatched placeholders will be left as-is.
|
||||||
|
* @param errorMessageArgs the arguments to be substituted into the message template. Arguments
|
||||||
|
* are converted to strings using {@link String#valueOf(Object)}.
|
||||||
|
* @return {@code reference}, guaranteed to be non-null, for convenience
|
||||||
|
* @throws VerifyException if {@code reference} is {@code null}
|
||||||
|
* @see Preconditions#checkNotNull Preconditions.checkNotNull()
|
||||||
|
*/
|
||||||
|
public static <T> T verifyNotNull(
|
||||||
|
T reference,
|
||||||
|
String errorMessageTemplate,
|
||||||
|
Object ... errorMessageArgs) {
|
||||||
|
verify(reference != null, errorMessageTemplate, errorMessageArgs);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(kevinb): consider <T> T verifySingleton(Iterable<T>) to take over for
|
||||||
|
// Iterables.getOnlyElement()
|
||||||
|
|
||||||
|
private Verify() {}
|
||||||
|
}
|
55
src/main/java/com/google/common/base/VerifyException.java
Normal file
55
src/main/java/com/google/common/base/VerifyException.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown upon the failure of a <a
|
||||||
|
* href="https://github.com/google/guava/wiki/ConditionalFailuresExplained">verification check</a>,
|
||||||
|
* including those performed by the convenience methods of the {@link Verify} class.
|
||||||
|
*
|
||||||
|
* @since 17.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public class VerifyException extends RuntimeException {
|
||||||
|
/** Constructs a {@code VerifyException} with no message. */
|
||||||
|
public VerifyException() {}
|
||||||
|
|
||||||
|
/** Constructs a {@code VerifyException} with the message {@code message}. */
|
||||||
|
public VerifyException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@code VerifyException} with the cause {@code cause} and a message that is {@code
|
||||||
|
* null} if {@code cause} is null, and {@code cause.toString()} otherwise.
|
||||||
|
*
|
||||||
|
* @since 19.0
|
||||||
|
*/
|
||||||
|
public VerifyException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@code VerifyException} with the message {@code message} and the cause {@code
|
||||||
|
* cause}.
|
||||||
|
*
|
||||||
|
* @since 19.0
|
||||||
|
*/
|
||||||
|
public VerifyException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
234
src/main/java/com/google/common/base/internal/Finalizer.java
Normal file
234
src/main/java/com/google/common/base/internal/Finalizer.java
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.base.internal;
|
||||||
|
|
||||||
|
import java.lang.ref.PhantomReference;
|
||||||
|
import java.lang.ref.Reference;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread that finalizes referents. All references should implement {@code
|
||||||
|
* com.google.common.base.FinalizableReference}.
|
||||||
|
*
|
||||||
|
* <p>While this class is public, we consider it to be *internal* and not part of our published API.
|
||||||
|
* It is public so we can access it reflectively across class loaders in secure environments.
|
||||||
|
*
|
||||||
|
* <p>This class can't depend on other Guava code. If we were to load this class in the same class
|
||||||
|
* loader as the rest of Guava, this thread would keep an indirect strong reference to the class
|
||||||
|
* loader and prevent it from being garbage collected. This poses a problem for environments where
|
||||||
|
* you want to throw away the class loader. For example, dynamically reloading a web application or
|
||||||
|
* unloading an OSGi bundle.
|
||||||
|
*
|
||||||
|
* <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class in its own class
|
||||||
|
* loader. That way, this class doesn't prevent the main class loader from getting garbage
|
||||||
|
* collected, and this class can detect when the main class loader has been garbage collected and
|
||||||
|
* stop itself.
|
||||||
|
*/
|
||||||
|
public class Finalizer implements Runnable {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(Finalizer.class.getName());
|
||||||
|
|
||||||
|
/** Name of FinalizableReference.class. */
|
||||||
|
private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively.
|
||||||
|
*
|
||||||
|
* @param finalizableReferenceClass FinalizableReference.class.
|
||||||
|
* @param queue a reference queue that the thread will poll.
|
||||||
|
* @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued
|
||||||
|
* either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its
|
||||||
|
* close() method is called.
|
||||||
|
*/
|
||||||
|
public static void startFinalizer(
|
||||||
|
Class<?> finalizableReferenceClass,
|
||||||
|
ReferenceQueue<Object> queue,
|
||||||
|
PhantomReference<Object> frqReference) {
|
||||||
|
/*
|
||||||
|
* We use FinalizableReference.class for two things:
|
||||||
|
*
|
||||||
|
* 1) To invoke FinalizableReference.finalizeReferent()
|
||||||
|
*
|
||||||
|
* 2) To detect when FinalizableReference's class loader has to be garbage collected, at which
|
||||||
|
* point, Finalizer can stop running
|
||||||
|
*/
|
||||||
|
if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
|
||||||
|
throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference);
|
||||||
|
String threadName = Finalizer.class.getName();
|
||||||
|
Thread thread = null;
|
||||||
|
if (bigThreadConstructor != null) {
|
||||||
|
try {
|
||||||
|
boolean inheritThreadLocals = false;
|
||||||
|
long defaultStackSize = 0;
|
||||||
|
thread =
|
||||||
|
bigThreadConstructor.newInstance(
|
||||||
|
(ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(
|
||||||
|
Level.INFO, "Failed to create a thread without inherited thread-local values", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (thread == null) {
|
||||||
|
thread = new Thread((ThreadGroup) null, finalizer, threadName);
|
||||||
|
}
|
||||||
|
thread.setDaemon(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (inheritableThreadLocals != null) {
|
||||||
|
inheritableThreadLocals.set(thread, null);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(
|
||||||
|
Level.INFO,
|
||||||
|
"Failed to clear thread local values inherited by reference finalizer thread.",
|
||||||
|
t);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final WeakReference<Class<?>> finalizableReferenceClassReference;
|
||||||
|
private final PhantomReference<Object> frqReference;
|
||||||
|
private final ReferenceQueue<Object> queue;
|
||||||
|
|
||||||
|
// By preference, we will use the Thread constructor that has an `inheritThreadLocals` parameter.
|
||||||
|
// But before Java 9, our only way not to inherit ThreadLocals is to zap them after the thread
|
||||||
|
// is created, by accessing a private field.
|
||||||
|
private static final Constructor<Thread> bigThreadConstructor =
|
||||||
|
getBigThreadConstructor();
|
||||||
|
|
||||||
|
private static final Field inheritableThreadLocals =
|
||||||
|
(bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null;
|
||||||
|
|
||||||
|
/** Constructs a new finalizer thread. */
|
||||||
|
private Finalizer(
|
||||||
|
Class<?> finalizableReferenceClass,
|
||||||
|
ReferenceQueue<Object> queue,
|
||||||
|
PhantomReference<Object> frqReference) {
|
||||||
|
this.queue = queue;
|
||||||
|
|
||||||
|
this.finalizableReferenceClassReference =
|
||||||
|
new WeakReference<Class<?>>(finalizableReferenceClass);
|
||||||
|
|
||||||
|
// Keep track of the FRQ that started us so we know when to stop.
|
||||||
|
this.frqReference = frqReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loops continuously, pulling references off the queue and cleaning them up. */
|
||||||
|
@SuppressWarnings("InfiniteLoopStatement")
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
if (!cleanUp(queue.remove())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up a single reference. Catches and logs all throwables.
|
||||||
|
*
|
||||||
|
* @return true if the caller should continue, false if the associated FinalizableReferenceQueue
|
||||||
|
* is no longer referenced.
|
||||||
|
*/
|
||||||
|
private boolean cleanUp(Reference<?> reference) {
|
||||||
|
Method finalizeReferentMethod = getFinalizeReferentMethod();
|
||||||
|
if (finalizeReferentMethod == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
/*
|
||||||
|
* This is for the benefit of phantom references. Weak and soft references will have already
|
||||||
|
* been cleared by this point.
|
||||||
|
*/
|
||||||
|
reference.clear();
|
||||||
|
|
||||||
|
if (reference == frqReference) {
|
||||||
|
/*
|
||||||
|
* The client no longer has a reference to the FinalizableReferenceQueue. We can stop.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
finalizeReferentMethod.invoke(reference);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop as long as we have references available so as not to waste CPU looking up the Method
|
||||||
|
* over and over again.
|
||||||
|
*/
|
||||||
|
} while ((reference = queue.poll()) != null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Looks up FinalizableReference.finalizeReferent() method. */
|
||||||
|
private Method getFinalizeReferentMethod() {
|
||||||
|
Class<?> finalizableReferenceClass = finalizableReferenceClassReference.get();
|
||||||
|
if (finalizableReferenceClass == null) {
|
||||||
|
/*
|
||||||
|
* FinalizableReference's class loader was reclaimed. While there's a chance that other
|
||||||
|
* finalizable references could be enqueued subsequently (at which point the class loader
|
||||||
|
* would be resurrected by virtue of us having a strong reference to it), we should pretty
|
||||||
|
* much just shut down and make sure we don't keep it alive any longer than necessary.
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return finalizableReferenceClass.getMethod("finalizeReferent");
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getInheritableThreadLocalsField() {
|
||||||
|
try {
|
||||||
|
Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals");
|
||||||
|
inheritableThreadLocals.setAccessible(true);
|
||||||
|
return inheritableThreadLocals;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(
|
||||||
|
Level.INFO,
|
||||||
|
"Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will "
|
||||||
|
+ "inherit thread local values.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Constructor<Thread> getBigThreadConstructor() {
|
||||||
|
try {
|
||||||
|
return Thread.class.getConstructor(
|
||||||
|
ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
265
src/main/java/com/google/common/cache/AbstractCache.java
vendored
Normal file
265
src/main/java/com/google/common/cache/AbstractCache.java
vendored
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@code Cache} interface to minimize the
|
||||||
|
* effort required to implement this interface.
|
||||||
|
*
|
||||||
|
* <p>To implement a cache, the programmer needs only to extend this class and provide an
|
||||||
|
* implementation for the {@link #put} and {@link #getIfPresent} methods. {@link #getAllPresent} is
|
||||||
|
* implemented in terms of {@link #getIfPresent}; {@link #putAll} is implemented in terms of {@link
|
||||||
|
* #put}, {@link #invalidateAll(Iterable)} is implemented in terms of {@link #invalidate}. The
|
||||||
|
* method {@link #cleanUp} is a no-op. All other methods throw an {@link
|
||||||
|
* UnsupportedOperationException}.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||||
|
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected AbstractCache() {}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This implementation of {@code getAllPresent} lacks any insight into the internal cache data
|
||||||
|
* structure, and is thus forced to return the query keys instead of the cached keys. This is only
|
||||||
|
* possible with an unsafe cast which requires {@code keys} to actually be of type {@code K}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) {
|
||||||
|
Map<K, V> result = Maps.newLinkedHashMap();
|
||||||
|
for (Object key : keys) {
|
||||||
|
if (!result.containsKey(key)) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
K castKey = (K) key;
|
||||||
|
V value = getIfPresent(key);
|
||||||
|
if (value != null) {
|
||||||
|
result.put(castKey, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ImmutableMap.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public void put(K key, V value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 12.0 */
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> m) {
|
||||||
|
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
|
||||||
|
put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanUp() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long size() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public void invalidateAll(Iterable<?> keys) {
|
||||||
|
for (Object key : keys) {
|
||||||
|
invalidate(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateAll() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CacheStats stats() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConcurrentMap<K, V> asMap() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accumulates statistics during the operation of a {@link Cache} for presentation by {@link
|
||||||
|
* Cache#stats}. This is solely intended for consumption by {@code Cache} implementors.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public interface StatsCounter {
|
||||||
|
/**
|
||||||
|
* Records cache hits. This should be called when a cache request returns a cached value.
|
||||||
|
*
|
||||||
|
* @param count the number of hits to record
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
void recordHits(int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records cache misses. This should be called when a cache request returns a value that was not
|
||||||
|
* found in the cache. This method should be called by the loading thread, as well as by threads
|
||||||
|
* blocking on the load. Multiple concurrent calls to {@link Cache} lookup methods with the same
|
||||||
|
* key on an absent value should result in a single call to either {@code recordLoadSuccess} or
|
||||||
|
* {@code recordLoadException} and multiple calls to this method, despite all being served by
|
||||||
|
* the results of a single load operation.
|
||||||
|
*
|
||||||
|
* @param count the number of misses to record
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
void recordMisses(int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the successful load of a new entry. This should be called when a cache request causes
|
||||||
|
* an entry to be loaded, and the loading completes successfully. In contrast to {@link
|
||||||
|
* #recordMisses}, this method should only be called by the loading thread.
|
||||||
|
*
|
||||||
|
* @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
|
||||||
|
* value
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||||
|
void recordLoadSuccess(long loadTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the failed load of a new entry. This should be called when a cache request causes an
|
||||||
|
* entry to be loaded, but an exception is thrown while loading the entry. In contrast to {@link
|
||||||
|
* #recordMisses}, this method should only be called by the loading thread.
|
||||||
|
*
|
||||||
|
* @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
|
||||||
|
* value prior to an exception being thrown
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||||
|
void recordLoadException(long loadTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the eviction of an entry from the cache. This should only been called when an entry
|
||||||
|
* is evicted due to the cache's eviction strategy, and not as a result of manual {@linkplain
|
||||||
|
* Cache#invalidate invalidations}.
|
||||||
|
*/
|
||||||
|
void recordEviction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as
|
||||||
|
* it may be interleaved with update operations.
|
||||||
|
*/
|
||||||
|
CacheStats snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public static final class SimpleStatsCounter implements StatsCounter {
|
||||||
|
private final LongAddable hitCount = LongAddables.create();
|
||||||
|
private final LongAddable missCount = LongAddables.create();
|
||||||
|
private final LongAddable loadSuccessCount = LongAddables.create();
|
||||||
|
private final LongAddable loadExceptionCount = LongAddables.create();
|
||||||
|
private final LongAddable totalLoadTime = LongAddables.create();
|
||||||
|
private final LongAddable evictionCount = LongAddables.create();
|
||||||
|
|
||||||
|
/** Constructs an instance with all counts initialized to zero. */
|
||||||
|
public SimpleStatsCounter() {}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public void recordHits(int count) {
|
||||||
|
hitCount.add(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public void recordMisses(int count) {
|
||||||
|
missCount.add(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("GoodTime") // b/122668874
|
||||||
|
@Override
|
||||||
|
public void recordLoadSuccess(long loadTime) {
|
||||||
|
loadSuccessCount.increment();
|
||||||
|
totalLoadTime.add(loadTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("GoodTime") // b/122668874
|
||||||
|
@Override
|
||||||
|
public void recordLoadException(long loadTime) {
|
||||||
|
loadExceptionCount.increment();
|
||||||
|
totalLoadTime.add(loadTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recordEviction() {
|
||||||
|
evictionCount.increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CacheStats snapshot() {
|
||||||
|
return new CacheStats(
|
||||||
|
negativeToMaxValue(hitCount.sum()),
|
||||||
|
negativeToMaxValue(missCount.sum()),
|
||||||
|
negativeToMaxValue(loadSuccessCount.sum()),
|
||||||
|
negativeToMaxValue(loadExceptionCount.sum()),
|
||||||
|
negativeToMaxValue(totalLoadTime.sum()),
|
||||||
|
negativeToMaxValue(evictionCount.sum()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns {@code value}, if non-negative. Otherwise, returns {@link Long#MAX_VALUE}. */
|
||||||
|
private static long negativeToMaxValue(long value) {
|
||||||
|
return (value >= 0) ? value : Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Increments all counters by the values in {@code other}. */
|
||||||
|
public void incrementBy(StatsCounter other) {
|
||||||
|
CacheStats otherStats = other.snapshot();
|
||||||
|
hitCount.add(otherStats.hitCount());
|
||||||
|
missCount.add(otherStats.missCount());
|
||||||
|
loadSuccessCount.add(otherStats.loadSuccessCount());
|
||||||
|
loadExceptionCount.add(otherStats.loadExceptionCount());
|
||||||
|
totalLoadTime.add(otherStats.totalLoadTime());
|
||||||
|
evictionCount.add(otherStats.evictionCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
src/main/java/com/google/common/cache/AbstractLoadingCache.java
vendored
Normal file
76
src/main/java/com/google/common/cache/AbstractLoadingCache.java
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@code Cache} interface to minimize the
|
||||||
|
* effort required to implement this interface.
|
||||||
|
*
|
||||||
|
* <p>To implement a cache, the programmer needs only to extend this class and provide an
|
||||||
|
* implementation for the {@link #get(Object)} and {@link #getIfPresent} methods. {@link
|
||||||
|
* #getUnchecked}, {@link #get(Object, Callable)}, and {@link #getAll} are implemented in terms of
|
||||||
|
* {@code get}; {@link #getAllPresent} is implemented in terms of {@code getIfPresent}; {@link
|
||||||
|
* #putAll} is implemented in terms of {@link #put}, {@link #invalidateAll(Iterable)} is implemented
|
||||||
|
* in terms of {@link #invalidate}. The method {@link #cleanUp} is a no-op. All other methods throw
|
||||||
|
* an {@link UnsupportedOperationException}.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public abstract class AbstractLoadingCache<K, V> extends AbstractCache<K, V>
|
||||||
|
implements LoadingCache<K, V> {
|
||||||
|
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected AbstractLoadingCache() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getUnchecked(K key) {
|
||||||
|
try {
|
||||||
|
return get(key);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new UncheckedExecutionException(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
|
||||||
|
Map<K, V> result = Maps.newLinkedHashMap();
|
||||||
|
for (K key : keys) {
|
||||||
|
if (!result.containsKey(key)) {
|
||||||
|
result.put(key, get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ImmutableMap.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final V apply(K key) {
|
||||||
|
return getUnchecked(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh(K key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
169
src/main/java/com/google/common/cache/Cache.java
vendored
Normal file
169
src/main/java/com/google/common/cache/Cache.java
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.util.concurrent.ExecutionError;
|
||||||
|
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A semi-persistent mapping from keys to values. Cache entries are manually added using {@link
|
||||||
|
* #get(Object, Callable)} or {@link #put(Object, Object)}, and are stored in the cache until either
|
||||||
|
* evicted or manually invalidated. The common way to build instances is using {@link CacheBuilder}.
|
||||||
|
*
|
||||||
|
* <p>Implementations of this interface are expected to be thread-safe, and can be safely accessed
|
||||||
|
* by multiple concurrent threads.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public interface Cache<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value associated with {@code key} in this cache, or {@code null} if there is no
|
||||||
|
* cached value for {@code key}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
V getIfPresent(Object key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value associated with {@code key} in this cache, obtaining that value from {@code
|
||||||
|
* loader} if necessary. The method improves upon the conventional "if cached, return; otherwise
|
||||||
|
* create, cache and return" pattern. For further improvements, use {@link LoadingCache} and its
|
||||||
|
* {@link LoadingCache#get(Object) get(K)} method instead of this one.
|
||||||
|
*
|
||||||
|
* <p>Among the improvements that this method and {@code LoadingCache.get(K)} both provide are:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@linkplain LoadingCache#get(Object) awaiting the result of a pending load} rather than
|
||||||
|
* starting a redundant one
|
||||||
|
* <li>eliminating the error-prone caching boilerplate
|
||||||
|
* <li>tracking load {@linkplain #stats statistics}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Among the further improvements that {@code LoadingCache} can provide but this method cannot:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>consolidation of the loader logic to {@linkplain CacheBuilder#build(CacheLoader) a single
|
||||||
|
* authoritative location}
|
||||||
|
* <li>{@linkplain LoadingCache#refresh refreshing of entries}, including {@linkplain
|
||||||
|
* CacheBuilder#refreshAfterWrite automated refreshing}
|
||||||
|
* <li>{@linkplain LoadingCache#getAll bulk loading requests}, including {@linkplain
|
||||||
|
* CacheLoader#loadAll bulk loading implementations}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> For any given key, every {@code loader} used with it should compute the same
|
||||||
|
* value. Otherwise, a call that passes one {@code loader} may return the result of another call
|
||||||
|
* with a differently behaving {@code loader}. For example, a call that requests a short timeout
|
||||||
|
* for an RPC may wait for a similar call that requests a long timeout, or a call by an
|
||||||
|
* unprivileged user may return a resource accessible only to a privileged user making a similar
|
||||||
|
* call. To prevent this problem, create a key object that includes all values that affect the
|
||||||
|
* result of the query. Or use {@code LoadingCache.get(K)}, which lacks the ability to refer to
|
||||||
|
* state other than that in the key.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> as with {@link CacheLoader#load}, {@code loader} <b>must not</b> return
|
||||||
|
* {@code null}; it may either return a non-null value or throw an exception.
|
||||||
|
*
|
||||||
|
* <p>No observable state associated with this cache is modified until loading completes.
|
||||||
|
*
|
||||||
|
* @throws ExecutionException if a checked exception was thrown while loading the value
|
||||||
|
* @throws UncheckedExecutionException if an unchecked exception was thrown while loading the
|
||||||
|
* value
|
||||||
|
* @throws ExecutionError if an error was thrown while loading the value
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
V get(K key, Callable<? extends V> loader) throws ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of the values associated with {@code keys} in this cache. The returned map will
|
||||||
|
* only contain entries which are already present in the cache.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
ImmutableMap<K, V> getAllPresent(Iterable<?> keys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates {@code value} with {@code key} in this cache. If the cache previously contained a
|
||||||
|
* value associated with {@code key}, the old value is replaced by {@code value}.
|
||||||
|
*
|
||||||
|
* <p>Prefer {@link #get(Object, Callable)} when using the conventional "if cached, return;
|
||||||
|
* otherwise create, cache and return" pattern.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
void put(K key, V value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all of the mappings from the specified map to the cache. The effect of this call is
|
||||||
|
* equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key
|
||||||
|
* {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined
|
||||||
|
* if the specified map is modified while the operation is in progress.
|
||||||
|
*
|
||||||
|
* @since 12.0
|
||||||
|
*/
|
||||||
|
void putAll(Map<? extends K, ? extends V> m);
|
||||||
|
|
||||||
|
/** Discards any cached value for key {@code key}. */
|
||||||
|
void invalidate(Object key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discards any cached values for keys {@code keys}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
void invalidateAll(Iterable<?> keys);
|
||||||
|
|
||||||
|
/** Discards all entries in the cache. */
|
||||||
|
void invalidateAll();
|
||||||
|
|
||||||
|
/** Returns the approximate number of entries in this cache. */
|
||||||
|
long size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a current snapshot of this cache's cumulative statistics, or a set of default values if
|
||||||
|
* the cache is not recording statistics. All statistics begin at zero and never decrease over the
|
||||||
|
* lifetime of the cache.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> this cache may not be recording statistical data. For example, a cache
|
||||||
|
* created using {@link CacheBuilder} only does so if the {@link CacheBuilder#recordStats} method
|
||||||
|
* was called. If statistics are not being recorded, a {@code CacheStats} instance with zero for
|
||||||
|
* all values is returned.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CacheStats stats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to
|
||||||
|
* the map directly affect the cache.
|
||||||
|
*
|
||||||
|
* <p>Iterators from the returned map are at least <i>weakly consistent</i>: they are safe for
|
||||||
|
* concurrent use, but if the cache is modified (including by eviction) after the iterator is
|
||||||
|
* created, it is undefined which of the changes (if any) will be reflected in that iterator.
|
||||||
|
*/
|
||||||
|
ConcurrentMap<K, V> asMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs any pending maintenance operations needed by the cache. Exactly which activities are
|
||||||
|
* performed -- if any -- is implementation-dependent.
|
||||||
|
*/
|
||||||
|
void cleanUp();
|
||||||
|
}
|
1055
src/main/java/com/google/common/cache/CacheBuilder.java
vendored
Normal file
1055
src/main/java/com/google/common/cache/CacheBuilder.java
vendored
Normal file
File diff suppressed because it is too large
Load diff
480
src/main/java/com/google/common/cache/CacheBuilderSpec.java
vendored
Normal file
480
src/main/java/com/google/common/cache/CacheBuilderSpec.java
vendored
Normal file
|
@ -0,0 +1,480 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.cache.LocalCache.Strength;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specification of a {@link CacheBuilder} configuration.
|
||||||
|
*
|
||||||
|
* <p>{@code CacheBuilderSpec} supports parsing configuration off of a string, which makes it
|
||||||
|
* especially useful for command-line configuration of a {@code CacheBuilder}.
|
||||||
|
*
|
||||||
|
* <p>The string syntax is a series of comma-separated keys or key-value pairs, each corresponding
|
||||||
|
* to a {@code CacheBuilder} method.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code concurrencyLevel=[integer]}: sets {@link CacheBuilder#concurrencyLevel}.
|
||||||
|
* <li>{@code initialCapacity=[integer]}: sets {@link CacheBuilder#initialCapacity}.
|
||||||
|
* <li>{@code maximumSize=[long]}: sets {@link CacheBuilder#maximumSize}.
|
||||||
|
* <li>{@code maximumWeight=[long]}: sets {@link CacheBuilder#maximumWeight}.
|
||||||
|
* <li>{@code expireAfterAccess=[duration]}: sets {@link CacheBuilder#expireAfterAccess}.
|
||||||
|
* <li>{@code expireAfterWrite=[duration]}: sets {@link CacheBuilder#expireAfterWrite}.
|
||||||
|
* <li>{@code refreshAfterWrite=[duration]}: sets {@link CacheBuilder#refreshAfterWrite}.
|
||||||
|
* <li>{@code weakKeys}: sets {@link CacheBuilder#weakKeys}.
|
||||||
|
* <li>{@code softValues}: sets {@link CacheBuilder#softValues}.
|
||||||
|
* <li>{@code weakValues}: sets {@link CacheBuilder#weakValues}.
|
||||||
|
* <li>{@code recordStats}: sets {@link CacheBuilder#recordStats}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>The set of supported keys will grow as {@code CacheBuilder} evolves, but existing keys will
|
||||||
|
* never be removed.
|
||||||
|
*
|
||||||
|
* <p>Durations are represented by an integer, followed by one of "d", "h", "m", or "s",
|
||||||
|
* representing days, hours, minutes, or seconds respectively. (There is currently no syntax to
|
||||||
|
* request expiration in milliseconds, microseconds, or nanoseconds.)
|
||||||
|
*
|
||||||
|
* <p>Whitespace before and after commas and equal signs is ignored. Keys may not be repeated; it is
|
||||||
|
* also illegal to use the following pairs of keys in a single value:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code maximumSize} and {@code maximumWeight}
|
||||||
|
* <li>{@code softValues} and {@code weakValues}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>{@code CacheBuilderSpec} does not support configuring {@code CacheBuilder} methods with
|
||||||
|
* non-value parameters. These must be configured in code.
|
||||||
|
*
|
||||||
|
* <p>A new {@code CacheBuilder} can be instantiated from a {@code CacheBuilderSpec} using {@link
|
||||||
|
* CacheBuilder#from(CacheBuilderSpec)} or {@link CacheBuilder#from(String)}.
|
||||||
|
*
|
||||||
|
* @author Adam Winer
|
||||||
|
* @since 12.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GoodTime") // lots of violations (nanosecond math)
|
||||||
|
@GwtIncompatible
|
||||||
|
public final class CacheBuilderSpec {
|
||||||
|
/** Parses a single value. */
|
||||||
|
private interface ValueParser {
|
||||||
|
void parse(CacheBuilderSpec spec, String key, String value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Splits each key-value pair. */
|
||||||
|
private static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults();
|
||||||
|
|
||||||
|
/** Splits the key from the value. */
|
||||||
|
private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults();
|
||||||
|
|
||||||
|
/** Map of names to ValueParser. */
|
||||||
|
private static final ImmutableMap<String, ValueParser> VALUE_PARSERS =
|
||||||
|
ImmutableMap.<String, ValueParser>builder()
|
||||||
|
.put("initialCapacity", new InitialCapacityParser())
|
||||||
|
.put("maximumSize", new MaximumSizeParser())
|
||||||
|
.put("maximumWeight", new MaximumWeightParser())
|
||||||
|
.put("concurrencyLevel", new ConcurrencyLevelParser())
|
||||||
|
.put("weakKeys", new KeyStrengthParser(Strength.WEAK))
|
||||||
|
.put("softValues", new ValueStrengthParser(Strength.SOFT))
|
||||||
|
.put("weakValues", new ValueStrengthParser(Strength.WEAK))
|
||||||
|
.put("recordStats", new RecordStatsParser())
|
||||||
|
.put("expireAfterAccess", new AccessDurationParser())
|
||||||
|
.put("expireAfterWrite", new WriteDurationParser())
|
||||||
|
.put("refreshAfterWrite", new RefreshDurationParser())
|
||||||
|
.put("refreshInterval", new RefreshDurationParser())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@VisibleForTesting Integer initialCapacity;
|
||||||
|
@VisibleForTesting Long maximumSize;
|
||||||
|
@VisibleForTesting Long maximumWeight;
|
||||||
|
@VisibleForTesting Integer concurrencyLevel;
|
||||||
|
@VisibleForTesting Strength keyStrength;
|
||||||
|
@VisibleForTesting Strength valueStrength;
|
||||||
|
@VisibleForTesting Boolean recordStats;
|
||||||
|
@VisibleForTesting long writeExpirationDuration;
|
||||||
|
@VisibleForTesting TimeUnit writeExpirationTimeUnit;
|
||||||
|
@VisibleForTesting long accessExpirationDuration;
|
||||||
|
@VisibleForTesting TimeUnit accessExpirationTimeUnit;
|
||||||
|
@VisibleForTesting long refreshDuration;
|
||||||
|
@VisibleForTesting TimeUnit refreshTimeUnit;
|
||||||
|
/** Specification; used for toParseableString(). */
|
||||||
|
private final String specification;
|
||||||
|
|
||||||
|
private CacheBuilderSpec(String specification) {
|
||||||
|
this.specification = specification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a CacheBuilderSpec from a string.
|
||||||
|
*
|
||||||
|
* @param cacheBuilderSpecification the string form
|
||||||
|
*/
|
||||||
|
public static CacheBuilderSpec parse(String cacheBuilderSpecification) {
|
||||||
|
CacheBuilderSpec spec = new CacheBuilderSpec(cacheBuilderSpecification);
|
||||||
|
if (!cacheBuilderSpecification.isEmpty()) {
|
||||||
|
for (String keyValuePair : KEYS_SPLITTER.split(cacheBuilderSpecification)) {
|
||||||
|
List<String> keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair));
|
||||||
|
checkArgument(!keyAndValue.isEmpty(), "blank key-value pair");
|
||||||
|
checkArgument(
|
||||||
|
keyAndValue.size() <= 2,
|
||||||
|
"key-value pair %s with more than one equals sign",
|
||||||
|
keyValuePair);
|
||||||
|
|
||||||
|
// Find the ValueParser for the current key.
|
||||||
|
String key = keyAndValue.get(0);
|
||||||
|
ValueParser valueParser = VALUE_PARSERS.get(key);
|
||||||
|
checkArgument(valueParser != null, "unknown key %s", key);
|
||||||
|
|
||||||
|
String value = keyAndValue.size() == 1 ? null : keyAndValue.get(1);
|
||||||
|
valueParser.parse(spec, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a CacheBuilderSpec that will prevent caching. */
|
||||||
|
public static CacheBuilderSpec disableCaching() {
|
||||||
|
// Maximum size of zero is one way to block caching
|
||||||
|
return CacheBuilderSpec.parse("maximumSize=0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a CacheBuilder configured according to this instance's specification. */
|
||||||
|
CacheBuilder<Object, Object> toCacheBuilder() {
|
||||||
|
CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
|
||||||
|
if (initialCapacity != null) {
|
||||||
|
builder.initialCapacity(initialCapacity);
|
||||||
|
}
|
||||||
|
if (maximumSize != null) {
|
||||||
|
builder.maximumSize(maximumSize);
|
||||||
|
}
|
||||||
|
if (maximumWeight != null) {
|
||||||
|
builder.maximumWeight(maximumWeight);
|
||||||
|
}
|
||||||
|
if (concurrencyLevel != null) {
|
||||||
|
builder.concurrencyLevel(concurrencyLevel);
|
||||||
|
}
|
||||||
|
if (keyStrength != null) {
|
||||||
|
switch (keyStrength) {
|
||||||
|
case WEAK:
|
||||||
|
builder.weakKeys();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valueStrength != null) {
|
||||||
|
switch (valueStrength) {
|
||||||
|
case SOFT:
|
||||||
|
builder.softValues();
|
||||||
|
break;
|
||||||
|
case WEAK:
|
||||||
|
builder.weakValues();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (recordStats != null && recordStats) {
|
||||||
|
builder.recordStats();
|
||||||
|
}
|
||||||
|
if (writeExpirationTimeUnit != null) {
|
||||||
|
builder.expireAfterWrite(writeExpirationDuration, writeExpirationTimeUnit);
|
||||||
|
}
|
||||||
|
if (accessExpirationTimeUnit != null) {
|
||||||
|
builder.expireAfterAccess(accessExpirationDuration, accessExpirationTimeUnit);
|
||||||
|
}
|
||||||
|
if (refreshTimeUnit != null) {
|
||||||
|
builder.refreshAfterWrite(refreshDuration, refreshTimeUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string that can be used to parse an equivalent {@code CacheBuilderSpec}. The order
|
||||||
|
* and form of this representation is not guaranteed, except that reparsing its output will
|
||||||
|
* produce a {@code CacheBuilderSpec} equal to this instance.
|
||||||
|
*/
|
||||||
|
public String toParsableString() {
|
||||||
|
return specification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation for this CacheBuilderSpec instance. The form of this
|
||||||
|
* representation is not guaranteed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this).addValue(toParsableString()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(
|
||||||
|
initialCapacity,
|
||||||
|
maximumSize,
|
||||||
|
maximumWeight,
|
||||||
|
concurrencyLevel,
|
||||||
|
keyStrength,
|
||||||
|
valueStrength,
|
||||||
|
recordStats,
|
||||||
|
durationInNanos(writeExpirationDuration, writeExpirationTimeUnit),
|
||||||
|
durationInNanos(accessExpirationDuration, accessExpirationTimeUnit),
|
||||||
|
durationInNanos(refreshDuration, refreshTimeUnit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof CacheBuilderSpec)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CacheBuilderSpec that = (CacheBuilderSpec) obj;
|
||||||
|
return Objects.equal(initialCapacity, that.initialCapacity)
|
||||||
|
&& Objects.equal(maximumSize, that.maximumSize)
|
||||||
|
&& Objects.equal(maximumWeight, that.maximumWeight)
|
||||||
|
&& Objects.equal(concurrencyLevel, that.concurrencyLevel)
|
||||||
|
&& Objects.equal(keyStrength, that.keyStrength)
|
||||||
|
&& Objects.equal(valueStrength, that.valueStrength)
|
||||||
|
&& Objects.equal(recordStats, that.recordStats)
|
||||||
|
&& Objects.equal(
|
||||||
|
durationInNanos(writeExpirationDuration, writeExpirationTimeUnit),
|
||||||
|
durationInNanos(that.writeExpirationDuration, that.writeExpirationTimeUnit))
|
||||||
|
&& Objects.equal(
|
||||||
|
durationInNanos(accessExpirationDuration, accessExpirationTimeUnit),
|
||||||
|
durationInNanos(that.accessExpirationDuration, that.accessExpirationTimeUnit))
|
||||||
|
&& Objects.equal(
|
||||||
|
durationInNanos(refreshDuration, refreshTimeUnit),
|
||||||
|
durationInNanos(that.refreshDuration, that.refreshTimeUnit));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an expiration duration/unit pair into a single Long for hashing and equality. Uses
|
||||||
|
* nanos to match CacheBuilder implementation.
|
||||||
|
*/
|
||||||
|
private static Long durationInNanos(long duration, TimeUnit unit) {
|
||||||
|
return (unit == null) ? null : unit.toNanos(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class for parsing integers. */
|
||||||
|
abstract static class IntegerParser implements ValueParser {
|
||||||
|
protected abstract void parseInteger(CacheBuilderSpec spec, int value);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(CacheBuilderSpec spec, String key, String value) {
|
||||||
|
checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
|
||||||
|
try {
|
||||||
|
parseInteger(spec, Integer.parseInt(value));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
format("key %s value set to %s, must be integer", key, value), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class for parsing integers. */
|
||||||
|
abstract static class LongParser implements ValueParser {
|
||||||
|
protected abstract void parseLong(CacheBuilderSpec spec, long value);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(CacheBuilderSpec spec, String key, String value) {
|
||||||
|
checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
|
||||||
|
try {
|
||||||
|
parseLong(spec, Long.parseLong(value));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
format("key %s value set to %s, must be integer", key, value), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse initialCapacity */
|
||||||
|
static class InitialCapacityParser extends IntegerParser {
|
||||||
|
@Override
|
||||||
|
protected void parseInteger(CacheBuilderSpec spec, int value) {
|
||||||
|
checkArgument(
|
||||||
|
spec.initialCapacity == null,
|
||||||
|
"initial capacity was already set to ",
|
||||||
|
spec.initialCapacity);
|
||||||
|
spec.initialCapacity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse maximumSize */
|
||||||
|
static class MaximumSizeParser extends LongParser {
|
||||||
|
@Override
|
||||||
|
protected void parseLong(CacheBuilderSpec spec, long value) {
|
||||||
|
checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize);
|
||||||
|
checkArgument(
|
||||||
|
spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight);
|
||||||
|
spec.maximumSize = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse maximumWeight */
|
||||||
|
static class MaximumWeightParser extends LongParser {
|
||||||
|
@Override
|
||||||
|
protected void parseLong(CacheBuilderSpec spec, long value) {
|
||||||
|
checkArgument(
|
||||||
|
spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight);
|
||||||
|
checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize);
|
||||||
|
spec.maximumWeight = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse concurrencyLevel */
|
||||||
|
static class ConcurrencyLevelParser extends IntegerParser {
|
||||||
|
@Override
|
||||||
|
protected void parseInteger(CacheBuilderSpec spec, int value) {
|
||||||
|
checkArgument(
|
||||||
|
spec.concurrencyLevel == null,
|
||||||
|
"concurrency level was already set to ",
|
||||||
|
spec.concurrencyLevel);
|
||||||
|
spec.concurrencyLevel = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse weakKeys */
|
||||||
|
static class KeyStrengthParser implements ValueParser {
|
||||||
|
private final Strength strength;
|
||||||
|
|
||||||
|
public KeyStrengthParser(Strength strength) {
|
||||||
|
this.strength = strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(CacheBuilderSpec spec, String key, String value) {
|
||||||
|
checkArgument(value == null, "key %s does not take values", key);
|
||||||
|
checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength);
|
||||||
|
spec.keyStrength = strength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse weakValues and softValues */
|
||||||
|
static class ValueStrengthParser implements ValueParser {
|
||||||
|
private final Strength strength;
|
||||||
|
|
||||||
|
public ValueStrengthParser(Strength strength) {
|
||||||
|
this.strength = strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(CacheBuilderSpec spec, String key, String value) {
|
||||||
|
checkArgument(value == null, "key %s does not take values", key);
|
||||||
|
checkArgument(
|
||||||
|
spec.valueStrength == null, "%s was already set to %s", key, spec.valueStrength);
|
||||||
|
|
||||||
|
spec.valueStrength = strength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse recordStats */
|
||||||
|
static class RecordStatsParser implements ValueParser {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(CacheBuilderSpec spec, String key, String value) {
|
||||||
|
checkArgument(value == null, "recordStats does not take values");
|
||||||
|
checkArgument(spec.recordStats == null, "recordStats already set");
|
||||||
|
spec.recordStats = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class for parsing times with durations */
|
||||||
|
abstract static class DurationParser implements ValueParser {
|
||||||
|
protected abstract void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(CacheBuilderSpec spec, String key, String value) {
|
||||||
|
checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
|
||||||
|
try {
|
||||||
|
char lastChar = value.charAt(value.length() - 1);
|
||||||
|
TimeUnit timeUnit;
|
||||||
|
switch (lastChar) {
|
||||||
|
case 'd':
|
||||||
|
timeUnit = TimeUnit.DAYS;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
timeUnit = TimeUnit.HOURS;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
timeUnit = TimeUnit.MINUTES;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
timeUnit = TimeUnit.SECONDS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
format(
|
||||||
|
"key %s invalid format. was %s, must end with one of [dDhHmMsS]", key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
long duration = Long.parseLong(value.substring(0, value.length() - 1));
|
||||||
|
parseDuration(spec, duration, timeUnit);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
format("key %s value set to %s, must be integer", key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse expireAfterAccess */
|
||||||
|
static class AccessDurationParser extends DurationParser {
|
||||||
|
@Override
|
||||||
|
protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
|
||||||
|
checkArgument(spec.accessExpirationTimeUnit == null, "expireAfterAccess already set");
|
||||||
|
spec.accessExpirationDuration = duration;
|
||||||
|
spec.accessExpirationTimeUnit = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse expireAfterWrite */
|
||||||
|
static class WriteDurationParser extends DurationParser {
|
||||||
|
@Override
|
||||||
|
protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
|
||||||
|
checkArgument(spec.writeExpirationTimeUnit == null, "expireAfterWrite already set");
|
||||||
|
spec.writeExpirationDuration = duration;
|
||||||
|
spec.writeExpirationTimeUnit = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse refreshAfterWrite */
|
||||||
|
static class RefreshDurationParser extends DurationParser {
|
||||||
|
@Override
|
||||||
|
protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
|
||||||
|
checkArgument(spec.refreshTimeUnit == null, "refreshAfterWrite already set");
|
||||||
|
spec.refreshDuration = duration;
|
||||||
|
spec.refreshTimeUnit = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(String format, Object... args) {
|
||||||
|
return String.format(Locale.ROOT, format, args);
|
||||||
|
}
|
||||||
|
}
|
251
src/main/java/com/google/common/cache/CacheLoader.java
vendored
Normal file
251
src/main/java/com/google/common/cache/CacheLoader.java
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes or retrieves values, based on a key, for use in populating a {@link LoadingCache}.
|
||||||
|
*
|
||||||
|
* <p>Most implementations will only need to implement {@link #load}. Other methods may be
|
||||||
|
* overridden as desired.
|
||||||
|
*
|
||||||
|
* <p>Usage example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* CacheLoader<Key, Graph> loader = new CacheLoader<Key, Graph>() {
|
||||||
|
* public Graph load(Key key) throws AnyException {
|
||||||
|
* return createExpensiveGraph(key);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* LoadingCache<Key, Graph> cache = CacheBuilder.newBuilder().build(loader);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>Since this example doesn't support reloading or bulk loading, it can also be specified much
|
||||||
|
* more simply:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* CacheLoader<Key, Graph> loader = CacheLoader.from(key -> createExpensiveGraph(key));
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
public abstract class CacheLoader<K, V> {
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected CacheLoader() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes or retrieves the value corresponding to {@code key}.
|
||||||
|
*
|
||||||
|
* @param key the non-null key whose value should be loaded
|
||||||
|
* @return the value associated with {@code key}; <b>must not be null</b>
|
||||||
|
* @throws Exception if unable to load the result
|
||||||
|
* @throws InterruptedException if this method is interrupted. {@code InterruptedException} is
|
||||||
|
* treated like any other {@code Exception} in all respects except that, when it is caught,
|
||||||
|
* the thread's interrupt status is set
|
||||||
|
*/
|
||||||
|
public abstract V load(K key) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes or retrieves a replacement value corresponding to an already-cached {@code key}. This
|
||||||
|
* method is called when an existing cache entry is refreshed by {@link
|
||||||
|
* CacheBuilder#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}.
|
||||||
|
*
|
||||||
|
* <p>This implementation synchronously delegates to {@link #load}. It is recommended that it be
|
||||||
|
* overridden with an asynchronous implementation when using {@link
|
||||||
|
* CacheBuilder#refreshAfterWrite}.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> <i>all exceptions thrown by this method will be logged and then swallowed</i>.
|
||||||
|
*
|
||||||
|
* @param key the non-null key whose value should be loaded
|
||||||
|
* @param oldValue the non-null old value corresponding to {@code key}
|
||||||
|
* @return the future new value associated with {@code key}; <b>must not be null, must not return
|
||||||
|
* null</b>
|
||||||
|
* @throws Exception if unable to reload the result
|
||||||
|
* @throws InterruptedException if this method is interrupted. {@code InterruptedException} is
|
||||||
|
* treated like any other {@code Exception} in all respects except that, when it is caught,
|
||||||
|
* the thread's interrupt status is set
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Futures
|
||||||
|
public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
|
||||||
|
checkNotNull(key);
|
||||||
|
checkNotNull(oldValue);
|
||||||
|
return Futures.immediateFuture(load(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes or retrieves the values corresponding to {@code keys}. This method is called by {@link
|
||||||
|
* LoadingCache#getAll}.
|
||||||
|
*
|
||||||
|
* <p>If the returned map doesn't contain all requested {@code keys} then the entries it does
|
||||||
|
* contain will be cached, but {@code getAll} will throw an exception. If the returned map
|
||||||
|
* contains extra keys not present in {@code keys} then all returned entries will be cached, but
|
||||||
|
* only the entries for {@code keys} will be returned from {@code getAll}.
|
||||||
|
*
|
||||||
|
* <p>This method should be overridden when bulk retrieval is significantly more efficient than
|
||||||
|
* many individual lookups. Note that {@link LoadingCache#getAll} will defer to individual calls
|
||||||
|
* to {@link LoadingCache#get} if this method is not overridden.
|
||||||
|
*
|
||||||
|
* @param keys the unique, non-null keys whose values should be loaded
|
||||||
|
* @return a map from each key in {@code keys} to the value associated with that key; <b>may not
|
||||||
|
* contain null values</b>
|
||||||
|
* @throws Exception if unable to load the result
|
||||||
|
* @throws InterruptedException if this method is interrupted. {@code InterruptedException} is
|
||||||
|
* treated like any other {@code Exception} in all respects except that, when it is caught,
|
||||||
|
* the thread's interrupt status is set
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public Map<K, V> loadAll(Iterable<? extends K> keys) throws Exception {
|
||||||
|
// This will be caught by getAll(), causing it to fall back to multiple calls to
|
||||||
|
// LoadingCache.get
|
||||||
|
throw new UnsupportedLoadingOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a cache loader that uses {@code function} to load keys, without supporting either
|
||||||
|
* reloading or bulk loading. This allows creating a cache loader using a lambda expression.
|
||||||
|
*
|
||||||
|
* @param function the function to be used for loading values; must never return {@code null}
|
||||||
|
* @return a cache loader that loads values by passing each key to {@code function}
|
||||||
|
*/
|
||||||
|
public static <K, V> CacheLoader<K, V> from(Function<K, V> function) {
|
||||||
|
return new FunctionToCacheLoader<>(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a cache loader based on an <i>existing</i> supplier instance. Note that there's no need
|
||||||
|
* to create a <i>new</i> supplier just to pass it in here; just subclass {@code CacheLoader} and
|
||||||
|
* implement {@link #load load} instead.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier to be used for loading values; must never return {@code null}
|
||||||
|
* @return a cache loader that loads values by calling {@link Supplier#get}, irrespective of the
|
||||||
|
* key
|
||||||
|
*/
|
||||||
|
public static <V> CacheLoader<Object, V> from(Supplier<V> supplier) {
|
||||||
|
return new SupplierToCacheLoader<V>(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FunctionToCacheLoader<K, V> extends CacheLoader<K, V>
|
||||||
|
implements Serializable {
|
||||||
|
private final Function<K, V> computingFunction;
|
||||||
|
|
||||||
|
public FunctionToCacheLoader(Function<K, V> computingFunction) {
|
||||||
|
this.computingFunction = checkNotNull(computingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V load(K key) {
|
||||||
|
return computingFunction.apply(checkNotNull(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code CacheLoader} which wraps {@code loader}, executing calls to {@link
|
||||||
|
* CacheLoader#reload} using {@code executor}.
|
||||||
|
*
|
||||||
|
* <p>This method is useful only when {@code loader.reload} has a synchronous implementation, such
|
||||||
|
* as {@linkplain #reload the default implementation}.
|
||||||
|
*
|
||||||
|
* @since 17.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible // Executor + Futures
|
||||||
|
public static <K, V> CacheLoader<K, V> asyncReloading(
|
||||||
|
final CacheLoader<K, V> loader, final Executor executor) {
|
||||||
|
checkNotNull(loader);
|
||||||
|
checkNotNull(executor);
|
||||||
|
return new CacheLoader<K, V>() {
|
||||||
|
@Override
|
||||||
|
public V load(K key) throws Exception {
|
||||||
|
return loader.load(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<V> reload(final K key, final V oldValue) throws Exception {
|
||||||
|
ListenableFutureTask<V> task =
|
||||||
|
ListenableFutureTask.create(
|
||||||
|
new Callable<V>() {
|
||||||
|
@Override
|
||||||
|
public V call() throws Exception {
|
||||||
|
return loader.reload(key, oldValue).get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
executor.execute(task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<K, V> loadAll(Iterable<? extends K> keys) throws Exception {
|
||||||
|
return loader.loadAll(keys);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class SupplierToCacheLoader<V> extends CacheLoader<Object, V>
|
||||||
|
implements Serializable {
|
||||||
|
private final Supplier<V> computingSupplier;
|
||||||
|
|
||||||
|
public SupplierToCacheLoader(Supplier<V> computingSupplier) {
|
||||||
|
this.computingSupplier = checkNotNull(computingSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V load(Object key) {
|
||||||
|
checkNotNull(key);
|
||||||
|
return computingSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by {@code loadAll()} to indicate that it is not supported.
|
||||||
|
*
|
||||||
|
* @since 19.0
|
||||||
|
*/
|
||||||
|
public static final class UnsupportedLoadingOperationException
|
||||||
|
extends UnsupportedOperationException {
|
||||||
|
// Package-private because this should only be thrown by loadAll() when it is not overridden.
|
||||||
|
// Cache implementors may want to catch it but should not need to be able to throw it.
|
||||||
|
UnsupportedLoadingOperationException() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown to indicate that an invalid response was returned from a call to {@link CacheLoader}.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public static final class InvalidCacheLoadException extends RuntimeException {
|
||||||
|
public InvalidCacheLoadException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
303
src/main/java/com/google/common/cache/CacheStats.java
vendored
Normal file
303
src/main/java/com/google/common/cache/CacheStats.java
vendored
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.math.LongMath.saturatedAdd;
|
||||||
|
import static com.google.common.math.LongMath.saturatedSubtract;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics about the performance of a {@link Cache}. Instances of this class are immutable.
|
||||||
|
*
|
||||||
|
* <p>Cache statistics are incremented according to the following rules:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>When a cache lookup encounters an existing cache entry {@code hitCount} is incremented.
|
||||||
|
* <li>When a cache lookup first encounters a missing cache entry, a new entry is loaded.
|
||||||
|
* <ul>
|
||||||
|
* <li>After successfully loading an entry {@code missCount} and {@code loadSuccessCount}
|
||||||
|
* are incremented, and the total loading time, in nanoseconds, is added to {@code
|
||||||
|
* totalLoadTime}.
|
||||||
|
* <li>When an exception is thrown while loading an entry, {@code missCount} and {@code
|
||||||
|
* loadExceptionCount} are incremented, and the total loading time, in nanoseconds, is
|
||||||
|
* added to {@code totalLoadTime}.
|
||||||
|
* <li>Cache lookups that encounter a missing cache entry that is still loading will wait
|
||||||
|
* for loading to complete (whether successful or not) and then increment {@code
|
||||||
|
* missCount}.
|
||||||
|
* </ul>
|
||||||
|
* <li>When an entry is evicted from the cache, {@code evictionCount} is incremented.
|
||||||
|
* <li>No stats are modified when a cache entry is invalidated or manually removed.
|
||||||
|
* <li>No stats are modified by operations invoked on the {@linkplain Cache#asMap asMap} view of
|
||||||
|
* the cache.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>A lookup is specifically defined as an invocation of one of the methods {@link
|
||||||
|
* LoadingCache#get(Object)}, {@link LoadingCache#getUnchecked(Object)}, {@link Cache#get(Object,
|
||||||
|
* Callable)}, or {@link LoadingCache#getAll(Iterable)}.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class CacheStats {
|
||||||
|
private final long hitCount;
|
||||||
|
private final long missCount;
|
||||||
|
private final long loadSuccessCount;
|
||||||
|
private final long loadExceptionCount;
|
||||||
|
|
||||||
|
@SuppressWarnings("GoodTime") // should be a java.time.Duration
|
||||||
|
private final long totalLoadTime;
|
||||||
|
|
||||||
|
private final long evictionCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@code CacheStats} instance.
|
||||||
|
*
|
||||||
|
* <p>Five parameters of the same type in a row is a bad thing, but this class is not constructed
|
||||||
|
* by end users and is too fine-grained for a builder.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||||
|
public CacheStats(
|
||||||
|
long hitCount,
|
||||||
|
long missCount,
|
||||||
|
long loadSuccessCount,
|
||||||
|
long loadExceptionCount,
|
||||||
|
long totalLoadTime,
|
||||||
|
long evictionCount) {
|
||||||
|
checkArgument(hitCount >= 0);
|
||||||
|
checkArgument(missCount >= 0);
|
||||||
|
checkArgument(loadSuccessCount >= 0);
|
||||||
|
checkArgument(loadExceptionCount >= 0);
|
||||||
|
checkArgument(totalLoadTime >= 0);
|
||||||
|
checkArgument(evictionCount >= 0);
|
||||||
|
|
||||||
|
this.hitCount = hitCount;
|
||||||
|
this.missCount = missCount;
|
||||||
|
this.loadSuccessCount = loadSuccessCount;
|
||||||
|
this.loadExceptionCount = loadExceptionCount;
|
||||||
|
this.totalLoadTime = totalLoadTime;
|
||||||
|
this.evictionCount = evictionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times {@link Cache} lookup methods have returned either a cached or
|
||||||
|
* uncached value. This is defined as {@code hitCount + missCount}.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
|
||||||
|
* guaranteed not to throw an exception). If you require specific handling, we recommend
|
||||||
|
* implementing your own stats collector.
|
||||||
|
*/
|
||||||
|
public long requestCount() {
|
||||||
|
return saturatedAdd(hitCount, missCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the number of times {@link Cache} lookup methods have returned a cached value. */
|
||||||
|
public long hitCount() {
|
||||||
|
return hitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ratio of cache requests which were hits. This is defined as {@code hitCount /
|
||||||
|
* requestCount}, or {@code 1.0} when {@code requestCount == 0}. Note that {@code hitRate +
|
||||||
|
* missRate =~ 1.0}.
|
||||||
|
*/
|
||||||
|
public double hitRate() {
|
||||||
|
long requestCount = requestCount();
|
||||||
|
return (requestCount == 0) ? 1.0 : (double) hitCount / requestCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times {@link Cache} lookup methods have returned an uncached (newly
|
||||||
|
* loaded) value, or null. Multiple concurrent calls to {@link Cache} lookup methods on an absent
|
||||||
|
* value can result in multiple misses, all returning the results of a single cache load
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
public long missCount() {
|
||||||
|
return missCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ratio of cache requests which were misses. This is defined as {@code missCount /
|
||||||
|
* requestCount}, or {@code 0.0} when {@code requestCount == 0}. Note that {@code hitRate +
|
||||||
|
* missRate =~ 1.0}. Cache misses include all requests which weren't cache hits, including
|
||||||
|
* requests which resulted in either successful or failed loading attempts, and requests which
|
||||||
|
* waited for other threads to finish loading. It is thus the case that {@code missCount >=
|
||||||
|
* loadSuccessCount + loadExceptionCount}. Multiple concurrent misses for the same key will result
|
||||||
|
* in a single load operation.
|
||||||
|
*/
|
||||||
|
public double missRate() {
|
||||||
|
long requestCount = requestCount();
|
||||||
|
return (requestCount == 0) ? 0.0 : (double) missCount / requestCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of times that {@link Cache} lookup methods attempted to load new
|
||||||
|
* values. This includes both successful load operations, as well as those that threw exceptions.
|
||||||
|
* This is defined as {@code loadSuccessCount + loadExceptionCount}.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
|
||||||
|
* guaranteed not to throw an exception). If you require specific handling, we recommend
|
||||||
|
* implementing your own stats collector.
|
||||||
|
*/
|
||||||
|
public long loadCount() {
|
||||||
|
return saturatedAdd(loadSuccessCount, loadExceptionCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times {@link Cache} lookup methods have successfully loaded a new value.
|
||||||
|
* This is usually incremented in conjunction with {@link #missCount}, though {@code missCount} is
|
||||||
|
* also incremented when an exception is encountered during cache loading (see {@link
|
||||||
|
* #loadExceptionCount}). Multiple concurrent misses for the same key will result in a single load
|
||||||
|
* operation. This may be incremented not in conjunction with {@code missCount} if the load occurs
|
||||||
|
* as a result of a refresh or if the cache loader returned more items than was requested. {@code
|
||||||
|
* missCount} may also be incremented not in conjunction with this (nor {@link
|
||||||
|
* #loadExceptionCount}) on calls to {@code getIfPresent}.
|
||||||
|
*/
|
||||||
|
public long loadSuccessCount() {
|
||||||
|
return loadSuccessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times {@link Cache} lookup methods threw an exception while loading a new
|
||||||
|
* value. This is usually incremented in conjunction with {@code missCount}, though {@code
|
||||||
|
* missCount} is also incremented when cache loading completes successfully (see {@link
|
||||||
|
* #loadSuccessCount}). Multiple concurrent misses for the same key will result in a single load
|
||||||
|
* operation. This may be incremented not in conjunction with {@code missCount} if the load occurs
|
||||||
|
* as a result of a refresh or if the cache loader returned more items than was requested. {@code
|
||||||
|
* missCount} may also be incremented not in conjunction with this (nor {@link #loadSuccessCount})
|
||||||
|
* on calls to {@code getIfPresent}.
|
||||||
|
*/
|
||||||
|
public long loadExceptionCount() {
|
||||||
|
return loadExceptionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ratio of cache loading attempts which threw exceptions. This is defined as {@code
|
||||||
|
* loadExceptionCount / (loadSuccessCount + loadExceptionCount)}, or {@code 0.0} when {@code
|
||||||
|
* loadSuccessCount + loadExceptionCount == 0}.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
|
||||||
|
* guaranteed not to throw an exception). If you require specific handling, we recommend
|
||||||
|
* implementing your own stats collector.
|
||||||
|
*/
|
||||||
|
public double loadExceptionRate() {
|
||||||
|
long totalLoadCount = saturatedAdd(loadSuccessCount, loadExceptionCount);
|
||||||
|
return (totalLoadCount == 0) ? 0.0 : (double) loadExceptionCount / totalLoadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of nanoseconds the cache has spent loading new values. This can be
|
||||||
|
* used to calculate the miss penalty. This value is increased every time {@code loadSuccessCount}
|
||||||
|
* or {@code loadExceptionCount} is incremented.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GoodTime") // should return a java.time.Duration
|
||||||
|
public long totalLoadTime() {
|
||||||
|
return totalLoadTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the average time spent loading new values. This is defined as {@code totalLoadTime /
|
||||||
|
* (loadSuccessCount + loadExceptionCount)}.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
|
||||||
|
* guaranteed not to throw an exception). If you require specific handling, we recommend
|
||||||
|
* implementing your own stats collector.
|
||||||
|
*/
|
||||||
|
public double averageLoadPenalty() {
|
||||||
|
long totalLoadCount = saturatedAdd(loadSuccessCount, loadExceptionCount);
|
||||||
|
return (totalLoadCount == 0) ? 0.0 : (double) totalLoadTime / totalLoadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times an entry has been evicted. This count does not include manual
|
||||||
|
* {@linkplain Cache#invalidate invalidations}.
|
||||||
|
*/
|
||||||
|
public long evictionCount() {
|
||||||
|
return evictionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@code CacheStats} representing the difference between this {@code CacheStats}
|
||||||
|
* and {@code other}. Negative values, which aren't supported by {@code CacheStats} will be
|
||||||
|
* rounded up to zero.
|
||||||
|
*/
|
||||||
|
public CacheStats minus(CacheStats other) {
|
||||||
|
return new CacheStats(
|
||||||
|
Math.max(0, saturatedSubtract(hitCount, other.hitCount)),
|
||||||
|
Math.max(0, saturatedSubtract(missCount, other.missCount)),
|
||||||
|
Math.max(0, saturatedSubtract(loadSuccessCount, other.loadSuccessCount)),
|
||||||
|
Math.max(0, saturatedSubtract(loadExceptionCount, other.loadExceptionCount)),
|
||||||
|
Math.max(0, saturatedSubtract(totalLoadTime, other.totalLoadTime)),
|
||||||
|
Math.max(0, saturatedSubtract(evictionCount, other.evictionCount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@code CacheStats} representing the sum of this {@code CacheStats} and {@code
|
||||||
|
* other}.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
|
||||||
|
* guaranteed not to throw an exception). If you require specific handling, we recommend
|
||||||
|
* implementing your own stats collector.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
public CacheStats plus(CacheStats other) {
|
||||||
|
return new CacheStats(
|
||||||
|
saturatedAdd(hitCount, other.hitCount),
|
||||||
|
saturatedAdd(missCount, other.missCount),
|
||||||
|
saturatedAdd(loadSuccessCount, other.loadSuccessCount),
|
||||||
|
saturatedAdd(loadExceptionCount, other.loadExceptionCount),
|
||||||
|
saturatedAdd(totalLoadTime, other.totalLoadTime),
|
||||||
|
saturatedAdd(evictionCount, other.evictionCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(
|
||||||
|
hitCount, missCount, loadSuccessCount, loadExceptionCount, totalLoadTime, evictionCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof CacheStats) {
|
||||||
|
CacheStats other = (CacheStats) object;
|
||||||
|
return hitCount == other.hitCount
|
||||||
|
&& missCount == other.missCount
|
||||||
|
&& loadSuccessCount == other.loadSuccessCount
|
||||||
|
&& loadExceptionCount == other.loadExceptionCount
|
||||||
|
&& totalLoadTime == other.totalLoadTime
|
||||||
|
&& evictionCount == other.evictionCount;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("hitCount", hitCount)
|
||||||
|
.add("missCount", missCount)
|
||||||
|
.add("loadSuccessCount", loadSuccessCount)
|
||||||
|
.add("loadExceptionCount", loadExceptionCount)
|
||||||
|
.add("totalLoadTime", totalLoadTime)
|
||||||
|
.add("evictionCount", evictionCount)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
128
src/main/java/com/google/common/cache/ForwardingCache.java
vendored
Normal file
128
src/main/java/com/google/common/cache/ForwardingCache.java
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.ForwardingObject;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache which forwards all its method calls to another cache. Subclasses should override one or
|
||||||
|
* more methods to modify the behavior of the backing cache as desired per the <a
|
||||||
|
* href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public abstract class ForwardingCache<K, V> extends ForwardingObject implements Cache<K, V> {
|
||||||
|
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected ForwardingCache() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected abstract Cache<K, V> delegate();
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public V getIfPresent(Object key) {
|
||||||
|
return delegate().getIfPresent(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
|
||||||
|
return delegate().get(key, valueLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) {
|
||||||
|
return delegate().getAllPresent(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public void put(K key, V value) {
|
||||||
|
delegate().put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 12.0 */
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> m) {
|
||||||
|
delegate().putAll(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate(Object key) {
|
||||||
|
delegate().invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 11.0 */
|
||||||
|
@Override
|
||||||
|
public void invalidateAll(Iterable<?> keys) {
|
||||||
|
delegate().invalidateAll(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateAll() {
|
||||||
|
delegate().invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long size() {
|
||||||
|
return delegate().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CacheStats stats() {
|
||||||
|
return delegate().stats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConcurrentMap<K, V> asMap() {
|
||||||
|
return delegate().asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanUp() {
|
||||||
|
delegate().cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simplified version of {@link ForwardingCache} where subclasses can pass in an already
|
||||||
|
* constructed {@link Cache} as the delegate.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public abstract static class SimpleForwardingCache<K, V> extends ForwardingCache<K, V> {
|
||||||
|
private final Cache<K, V> delegate;
|
||||||
|
|
||||||
|
protected SimpleForwardingCache(Cache<K, V> delegate) {
|
||||||
|
this.delegate = Preconditions.checkNotNull(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final Cache<K, V> delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
src/main/java/com/google/common/cache/ForwardingLoadingCache.java
vendored
Normal file
87
src/main/java/com/google/common/cache/ForwardingLoadingCache.java
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache which forwards all its method calls to another cache. Subclasses should override one or
|
||||||
|
* more methods to modify the behavior of the backing cache as desired per the <a
|
||||||
|
* href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>.
|
||||||
|
*
|
||||||
|
* <p>Note that {@link #get}, {@link #getUnchecked}, and {@link #apply} all expose the same
|
||||||
|
* underlying functionality, so should probably be overridden as a group.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public abstract class ForwardingLoadingCache<K, V> extends ForwardingCache<K, V>
|
||||||
|
implements LoadingCache<K, V> {
|
||||||
|
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected ForwardingLoadingCache() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected abstract LoadingCache<K, V> delegate();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(K key) throws ExecutionException {
|
||||||
|
return delegate().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getUnchecked(K key) {
|
||||||
|
return delegate().getUnchecked(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
|
||||||
|
return delegate().getAll(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V apply(K key) {
|
||||||
|
return delegate().apply(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh(K key) {
|
||||||
|
delegate().refresh(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simplified version of {@link ForwardingLoadingCache} where subclasses can pass in an already
|
||||||
|
* constructed {@link LoadingCache} as the delegate.
|
||||||
|
*
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
public abstract static class SimpleForwardingLoadingCache<K, V>
|
||||||
|
extends ForwardingLoadingCache<K, V> {
|
||||||
|
private final LoadingCache<K, V> delegate;
|
||||||
|
|
||||||
|
protected SimpleForwardingLoadingCache(LoadingCache<K, V> delegate) {
|
||||||
|
this.delegate = Preconditions.checkNotNull(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final LoadingCache<K, V> delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
160
src/main/java/com/google/common/cache/LoadingCache.java
vendored
Normal file
160
src/main/java/com/google/common/cache/LoadingCache.java
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.util.concurrent.ExecutionError;
|
||||||
|
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and
|
||||||
|
* are stored in the cache until either evicted or manually invalidated. The common way to build
|
||||||
|
* instances is using {@link CacheBuilder}.
|
||||||
|
*
|
||||||
|
* <p>Implementations of this interface are expected to be thread-safe, and can be safely accessed
|
||||||
|
* by multiple concurrent threads.
|
||||||
|
*
|
||||||
|
* <p>When evaluated as a {@link Function}, a cache yields the same result as invoking {@link
|
||||||
|
* #getUnchecked}.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public interface LoadingCache<K, V> extends Cache<K, V>, Function<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value associated with {@code key} in this cache, first loading that value if
|
||||||
|
* necessary. No observable state associated with this cache is modified until loading completes.
|
||||||
|
*
|
||||||
|
* <p>If another call to {@link #get} or {@link #getUnchecked} is currently loading the value for
|
||||||
|
* {@code key}, simply waits for that thread to finish and returns its loaded value. Note that
|
||||||
|
* multiple threads can concurrently load values for distinct keys.
|
||||||
|
*
|
||||||
|
* <p>Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#load} to load new values
|
||||||
|
* into the cache. Newly loaded values are added to the cache using {@code
|
||||||
|
* Cache.asMap().putIfAbsent} after loading has completed; if another value was associated with
|
||||||
|
* {@code key} while the new value was loading then a removal notification will be sent for the
|
||||||
|
* new value.
|
||||||
|
*
|
||||||
|
* <p>If the cache loader associated with this cache is known not to throw checked exceptions,
|
||||||
|
* then prefer {@link #getUnchecked} over this method.
|
||||||
|
*
|
||||||
|
* @throws ExecutionException if a checked exception was thrown while loading the value. ({@code
|
||||||
|
* ExecutionException} is thrown <a
|
||||||
|
* href="https://github.com/google/guava/wiki/CachesExplained#interruption">even if
|
||||||
|
* computation was interrupted by an {@code InterruptedException}</a>.)
|
||||||
|
* @throws UncheckedExecutionException if an unchecked exception was thrown while loading the
|
||||||
|
* value
|
||||||
|
* @throws ExecutionError if an error was thrown while loading the value
|
||||||
|
*/
|
||||||
|
V get(K key) throws ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value associated with {@code key} in this cache, first loading that value if
|
||||||
|
* necessary. No observable state associated with this cache is modified until loading completes.
|
||||||
|
* Unlike {@link #get}, this method does not throw a checked exception, and thus should only be
|
||||||
|
* used in situations where checked exceptions are not thrown by the cache loader.
|
||||||
|
*
|
||||||
|
* <p>If another call to {@link #get} or {@link #getUnchecked} is currently loading the value for
|
||||||
|
* {@code key}, simply waits for that thread to finish and returns its loaded value. Note that
|
||||||
|
* multiple threads can concurrently load values for distinct keys.
|
||||||
|
*
|
||||||
|
* <p>Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#load} to load new values
|
||||||
|
* into the cache. Newly loaded values are added to the cache using {@code
|
||||||
|
* Cache.asMap().putIfAbsent} after loading has completed; if another value was associated with
|
||||||
|
* {@code key} while the new value was loading then a removal notification will be sent for the
|
||||||
|
* new value.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> this method silently converts checked exceptions to unchecked exceptions,
|
||||||
|
* and should not be used with cache loaders which throw checked exceptions. In such cases use
|
||||||
|
* {@link #get} instead.
|
||||||
|
*
|
||||||
|
* @throws UncheckedExecutionException if an exception was thrown while loading the value. (As
|
||||||
|
* explained in the last paragraph above, this should be an unchecked exception only.)
|
||||||
|
* @throws ExecutionError if an error was thrown while loading the value
|
||||||
|
*/
|
||||||
|
V getUnchecked(K key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of the values associated with {@code keys}, creating or retrieving those values
|
||||||
|
* if necessary. The returned map contains entries that were already cached, combined with newly
|
||||||
|
* loaded entries; it will never contain null keys or values.
|
||||||
|
*
|
||||||
|
* <p>Caches loaded by a {@link CacheLoader} will issue a single request to {@link
|
||||||
|
* CacheLoader#loadAll} for all keys which are not already present in the cache. All entries
|
||||||
|
* returned by {@link CacheLoader#loadAll} will be stored in the cache, over-writing any
|
||||||
|
* previously cached values. This method will throw an exception if {@link CacheLoader#loadAll}
|
||||||
|
* returns {@code null}, returns a map containing null keys or values, or fails to return an entry
|
||||||
|
* for each requested key.
|
||||||
|
*
|
||||||
|
* <p>Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will
|
||||||
|
* be ignored.
|
||||||
|
*
|
||||||
|
* @throws ExecutionException if a checked exception was thrown while loading the value. ({@code
|
||||||
|
* ExecutionException} is thrown <a
|
||||||
|
* href="https://github.com/google/guava/wiki/CachesExplained#interruption">even if
|
||||||
|
* computation was interrupted by an {@code InterruptedException}</a>.)
|
||||||
|
* @throws UncheckedExecutionException if an unchecked exception was thrown while loading the
|
||||||
|
* values
|
||||||
|
* @throws ExecutionError if an error was thrown while loading the values
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Provided to satisfy the {@code Function} interface; use {@link #get} or {@link
|
||||||
|
* #getUnchecked} instead.
|
||||||
|
* @throws UncheckedExecutionException if an exception was thrown while loading the value. (As
|
||||||
|
* described in the documentation for {@link #getUnchecked}, {@code LoadingCache} should be
|
||||||
|
* used as a {@code Function} only with cache loaders that throw only unchecked exceptions.)
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
V apply(K key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a new value for key {@code key}, possibly asynchronously. While the new value is loading
|
||||||
|
* the previous value (if any) will continue to be returned by {@code get(key)} unless it is
|
||||||
|
* evicted. If the new value is loaded successfully it will replace the previous value in the
|
||||||
|
* cache; if an exception is thrown while refreshing the previous value will remain, <i>and the
|
||||||
|
* exception will be logged (using {@link java.util.logging.Logger}) and swallowed</i>.
|
||||||
|
*
|
||||||
|
* <p>Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#reload} if the cache
|
||||||
|
* currently contains a value for {@code key}, and {@link CacheLoader#load} otherwise. Loading is
|
||||||
|
* asynchronous only if {@link CacheLoader#reload} was overridden with an asynchronous
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* <p>Returns without doing anything if another thread is currently loading the value for {@code
|
||||||
|
* key}. If the cache loader associated with this cache performs refresh asynchronously then this
|
||||||
|
* method may return before refresh completes.
|
||||||
|
*
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
void refresh(K key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p><b>Note that although the view <i>is</i> modifiable, no method on the returned map will ever
|
||||||
|
* cause entries to be automatically loaded.</b>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
ConcurrentMap<K, V> asMap();
|
||||||
|
}
|
4994
src/main/java/com/google/common/cache/LocalCache.java
vendored
Normal file
4994
src/main/java/com/google/common/cache/LocalCache.java
vendored
Normal file
File diff suppressed because it is too large
Load diff
31
src/main/java/com/google/common/cache/LongAddable.java
vendored
Normal file
31
src/main/java/com/google/common/cache/LongAddable.java
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract interface for objects that can concurrently add longs.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
interface LongAddable {
|
||||||
|
void increment();
|
||||||
|
|
||||||
|
void add(long x);
|
||||||
|
|
||||||
|
long sum();
|
||||||
|
}
|
54
src/main/java/com/google/common/cache/LongAddables.java
vendored
Normal file
54
src/main/java/com/google/common/cache/LongAddables.java
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source of {@link LongAddable} objects that deals with GWT, Unsafe, and all that.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
final class LongAddables {
|
||||||
|
private static final Supplier<LongAddable> SUPPLIER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
SUPPLIER = PureJavaLongAddable::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LongAddable create() {
|
||||||
|
return SUPPLIER.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class PureJavaLongAddable extends AtomicLong implements LongAddable {
|
||||||
|
@Override
|
||||||
|
public void increment() {
|
||||||
|
getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(long x) {
|
||||||
|
getAndAdd(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long sum() {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
src/main/java/com/google/common/cache/ReferenceEntry.java
vendored
Normal file
111
src/main/java/com/google/common/cache/ReferenceEntry.java
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.cache.LocalCache.ValueReference;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entry in a reference map.
|
||||||
|
*
|
||||||
|
* <p>Entries in the map can be in the following states:
|
||||||
|
*
|
||||||
|
* <p>Valid:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Live: valid key/value are set
|
||||||
|
* <li>Loading: loading is pending
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Invalid:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Expired: time expired (key/value may still be set)
|
||||||
|
* <li>Collected: key/value was partially collected, but not yet cleaned up
|
||||||
|
* <li>Unset: marked as unset, awaiting cleanup or reuse
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
interface ReferenceEntry<K, V> {
|
||||||
|
/** Returns the value reference from this entry. */
|
||||||
|
ValueReference<K, V> getValueReference();
|
||||||
|
|
||||||
|
/** Sets the value reference for this entry. */
|
||||||
|
void setValueReference(ValueReference<K, V> valueReference);
|
||||||
|
|
||||||
|
/** Returns the next entry in the chain. */
|
||||||
|
|
||||||
|
ReferenceEntry<K, V> getNext();
|
||||||
|
|
||||||
|
/** Returns the entry's hash. */
|
||||||
|
int getHash();
|
||||||
|
|
||||||
|
/** Returns the key for this entry. */
|
||||||
|
|
||||||
|
K getKey();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used by entries that use access order. Access entries are maintained in a doubly-linked list.
|
||||||
|
* New entries are added at the tail of the list at write time; stale entries are expired from
|
||||||
|
* the head of the list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Returns the time that this entry was last accessed, in ns. */
|
||||||
|
@SuppressWarnings("GoodTime")
|
||||||
|
long getAccessTime();
|
||||||
|
|
||||||
|
/** Sets the entry access time in ns. */
|
||||||
|
@SuppressWarnings("GoodTime") // b/122668874
|
||||||
|
void setAccessTime(long time);
|
||||||
|
|
||||||
|
/** Returns the next entry in the access queue. */
|
||||||
|
ReferenceEntry<K, V> getNextInAccessQueue();
|
||||||
|
|
||||||
|
/** Sets the next entry in the access queue. */
|
||||||
|
void setNextInAccessQueue(ReferenceEntry<K, V> next);
|
||||||
|
|
||||||
|
/** Returns the previous entry in the access queue. */
|
||||||
|
ReferenceEntry<K, V> getPreviousInAccessQueue();
|
||||||
|
|
||||||
|
/** Sets the previous entry in the access queue. */
|
||||||
|
void setPreviousInAccessQueue(ReferenceEntry<K, V> previous);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implemented by entries that use write order. Write entries are maintained in a doubly-linked
|
||||||
|
* list. New entries are added at the tail of the list at write time and stale entries are
|
||||||
|
* expired from the head of the list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings("GoodTime")
|
||||||
|
/** Returns the time that this entry was last written, in ns. */
|
||||||
|
long getWriteTime();
|
||||||
|
|
||||||
|
/** Sets the entry write time in ns. */
|
||||||
|
@SuppressWarnings("GoodTime") // b/122668874
|
||||||
|
void setWriteTime(long time);
|
||||||
|
|
||||||
|
/** Returns the next entry in the write queue. */
|
||||||
|
ReferenceEntry<K, V> getNextInWriteQueue();
|
||||||
|
|
||||||
|
/** Sets the next entry in the write queue. */
|
||||||
|
void setNextInWriteQueue(ReferenceEntry<K, V> next);
|
||||||
|
|
||||||
|
/** Returns the previous entry in the write queue. */
|
||||||
|
ReferenceEntry<K, V> getPreviousInWriteQueue();
|
||||||
|
|
||||||
|
/** Sets the previous entry in the write queue. */
|
||||||
|
void setPreviousInWriteQueue(ReferenceEntry<K, V> previous);
|
||||||
|
}
|
94
src/main/java/com/google/common/cache/RemovalCause.java
vendored
Normal file
94
src/main/java/com/google/common/cache/RemovalCause.java
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reason why a cached entry was removed.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public enum RemovalCause {
|
||||||
|
/**
|
||||||
|
* The entry was manually removed by the user. This can result from the user invoking {@link
|
||||||
|
* Cache#invalidate}, {@link Cache#invalidateAll(Iterable)}, {@link Cache#invalidateAll()}, {@link
|
||||||
|
* Map#remove}, {@link ConcurrentMap#remove}, or {@link Iterator#remove}.
|
||||||
|
*/
|
||||||
|
EXPLICIT {
|
||||||
|
@Override
|
||||||
|
boolean wasEvicted() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry itself was not actually removed, but its value was replaced by the user. This can
|
||||||
|
* result from the user invoking {@link Cache#put}, {@link LoadingCache#refresh}, {@link Map#put},
|
||||||
|
* {@link Map#putAll}, {@link ConcurrentMap#replace(Object, Object)}, or {@link
|
||||||
|
* ConcurrentMap#replace(Object, Object, Object)}.
|
||||||
|
*/
|
||||||
|
REPLACED {
|
||||||
|
@Override
|
||||||
|
boolean wasEvicted() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry was removed automatically because its key or value was garbage-collected. This can
|
||||||
|
* occur when using {@link CacheBuilder#weakKeys}, {@link CacheBuilder#weakValues}, or {@link
|
||||||
|
* CacheBuilder#softValues}.
|
||||||
|
*/
|
||||||
|
COLLECTED {
|
||||||
|
@Override
|
||||||
|
boolean wasEvicted() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry's expiration timestamp has passed. This can occur when using {@link
|
||||||
|
* CacheBuilder#expireAfterWrite} or {@link CacheBuilder#expireAfterAccess}.
|
||||||
|
*/
|
||||||
|
EXPIRED {
|
||||||
|
@Override
|
||||||
|
boolean wasEvicted() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry was evicted due to size constraints. This can occur when using {@link
|
||||||
|
* CacheBuilder#maximumSize} or {@link CacheBuilder#maximumWeight}.
|
||||||
|
*/
|
||||||
|
SIZE {
|
||||||
|
@Override
|
||||||
|
boolean wasEvicted() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
|
||||||
|
* {@link #EXPLICIT} nor {@link #REPLACED}).
|
||||||
|
*/
|
||||||
|
abstract boolean wasEvicted();
|
||||||
|
}
|
47
src/main/java/com/google/common/cache/RemovalListener.java
vendored
Normal file
47
src/main/java/com/google/common/cache/RemovalListener.java
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that can receive a notification when an entry is removed from a cache. The removal
|
||||||
|
* resulting in notification could have occurred to an entry being manually removed or replaced, or
|
||||||
|
* due to eviction resulting from timed expiration, exceeding a maximum size, or garbage collection.
|
||||||
|
*
|
||||||
|
* <p>An instance may be called concurrently by multiple threads to process different entries.
|
||||||
|
* Implementations of this interface should avoid performing blocking calls or synchronizing on
|
||||||
|
* shared resources.
|
||||||
|
*
|
||||||
|
* @param <K> the most general type of keys this listener can listen for; for example {@code Object}
|
||||||
|
* if any key is acceptable
|
||||||
|
* @param <V> the most general type of values this listener can listen for; for example {@code
|
||||||
|
* Object} if any key is acceptable
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface RemovalListener<K, V> {
|
||||||
|
/**
|
||||||
|
* Notifies the listener that a removal occurred at some point in the past.
|
||||||
|
*
|
||||||
|
* <p>This does not always signify that the key is now absent from the cache, as it may have
|
||||||
|
* already been re-added.
|
||||||
|
*/
|
||||||
|
// Technically should accept RemovalNotification<? extends K, ? extends V>, but because
|
||||||
|
// RemovalNotification is guaranteed covariant, let's make users' lives simpler.
|
||||||
|
void onRemoval(RemovalNotification<K, V> notification);
|
||||||
|
}
|
57
src/main/java/com/google/common/cache/RemovalListeners.java
vendored
Normal file
57
src/main/java/com/google/common/cache/RemovalListeners.java
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of common removal listeners.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
public final class RemovalListeners {
|
||||||
|
|
||||||
|
private RemovalListeners() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code RemovalListener} which processes all eviction notifications using {@code
|
||||||
|
* executor}.
|
||||||
|
*
|
||||||
|
* @param listener the backing listener
|
||||||
|
* @param executor the executor with which removal notifications are asynchronously executed
|
||||||
|
*/
|
||||||
|
public static <K, V> RemovalListener<K, V> asynchronous(
|
||||||
|
final RemovalListener<K, V> listener, final Executor executor) {
|
||||||
|
checkNotNull(listener);
|
||||||
|
checkNotNull(executor);
|
||||||
|
return new RemovalListener<K, V>() {
|
||||||
|
@Override
|
||||||
|
public void onRemoval(final RemovalNotification<K, V> notification) {
|
||||||
|
executor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onRemoval(notification);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
69
src/main/java/com/google/common/cache/RemovalNotification.java
vendored
Normal file
69
src/main/java/com/google/common/cache/RemovalNotification.java
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A notification of the removal of a single entry. The key and/or value may be null if they were
|
||||||
|
* already garbage collected.
|
||||||
|
*
|
||||||
|
* <p>Like other {@code Entry} instances associated with {@code CacheBuilder}, this class holds
|
||||||
|
* strong references to the key and value, regardless of the type of references the cache may be
|
||||||
|
* using.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 10.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public final class RemovalNotification<K, V> extends SimpleImmutableEntry<K, V> {
|
||||||
|
private final RemovalCause cause;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RemovalNotification} for the given {@code key}/{@code value} pair, with
|
||||||
|
* the given {@code cause} for the removal. The {@code key} and/or {@code value} may be {@code
|
||||||
|
* null} if they were already garbage collected.
|
||||||
|
*
|
||||||
|
* @since 19.0
|
||||||
|
*/
|
||||||
|
public static <K, V> RemovalNotification<K, V> create(
|
||||||
|
K key, V value, RemovalCause cause) {
|
||||||
|
return new RemovalNotification(key, value, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RemovalNotification(K key, V value, RemovalCause cause) {
|
||||||
|
super(key, value);
|
||||||
|
this.cause = checkNotNull(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the cause for which the entry was removed. */
|
||||||
|
public RemovalCause getCause() {
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
|
||||||
|
* {@link RemovalCause#EXPLICIT} nor {@link RemovalCause#REPLACED}).
|
||||||
|
*/
|
||||||
|
public boolean wasEvicted() {
|
||||||
|
return cause.wasEvicted();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
36
src/main/java/com/google/common/cache/Weigher.java
vendored
Normal file
36
src/main/java/com/google/common/cache/Weigher.java
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.cache;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the weights of cache entries.
|
||||||
|
*
|
||||||
|
* @author Charles Fry
|
||||||
|
* @since 11.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Weigher<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the weight of a cache entry. There is no unit for entry weights; rather they are simply
|
||||||
|
* relative to each other.
|
||||||
|
*
|
||||||
|
* @return the weight of the entry; must be non-negative
|
||||||
|
*/
|
||||||
|
int weigh(K key, V value);
|
||||||
|
}
|
485
src/main/java/com/google/common/collect/AbstractBiMap.java
Normal file
485
src/main/java/com/google/common/collect/AbstractBiMap.java
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.common.collect.CollectPreconditions.checkRemove;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A general-purpose bimap implementation using any two backing {@code Map} instances.
|
||||||
|
*
|
||||||
|
* <p>Note that this class contains {@code equals()} calls that keep it from supporting {@code
|
||||||
|
* IdentityHashMap} backing maps.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @author Mike Bostock
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
|
||||||
|
implements BiMap<K, V>, Serializable {
|
||||||
|
|
||||||
|
private transient Map<K, V> delegate;
|
||||||
|
transient AbstractBiMap<V, K> inverse;
|
||||||
|
|
||||||
|
/** Package-private constructor for creating a map-backed bimap. */
|
||||||
|
AbstractBiMap(Map<K, V> forward, Map<V, K> backward) {
|
||||||
|
setDelegates(forward, backward);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Private constructor for inverse bimap. */
|
||||||
|
private AbstractBiMap(Map<K, V> backward, AbstractBiMap<V, K> forward) {
|
||||||
|
delegate = backward;
|
||||||
|
inverse = forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<K, V> delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns its input, or throws an exception if this is not a valid key. */
|
||||||
|
|
||||||
|
K checkKey(K key) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns its input, or throws an exception if this is not a valid value. */
|
||||||
|
|
||||||
|
V checkValue(V value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the delegate maps going in each direction. Called by the constructor and by
|
||||||
|
* subclasses during deserialization.
|
||||||
|
*/
|
||||||
|
void setDelegates(Map<K, V> forward, Map<V, K> backward) {
|
||||||
|
checkState(delegate == null);
|
||||||
|
checkState(inverse == null);
|
||||||
|
checkArgument(forward.isEmpty());
|
||||||
|
checkArgument(backward.isEmpty());
|
||||||
|
checkArgument(forward != backward);
|
||||||
|
delegate = forward;
|
||||||
|
inverse = makeInverse(backward);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractBiMap<V, K> makeInverse(Map<V, K> backward) {
|
||||||
|
return new Inverse<>(backward, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInverse(AbstractBiMap<V, K> inverse) {
|
||||||
|
this.inverse = inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query Operations (optimizations)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return inverse.containsKey(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modification Operations
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(K key, V value) {
|
||||||
|
return putInBothMaps(key, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V forcePut(K key, V value) {
|
||||||
|
return putInBothMaps(key, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private V putInBothMaps(K key, V value, boolean force) {
|
||||||
|
checkKey(key);
|
||||||
|
checkValue(value);
|
||||||
|
boolean containedKey = containsKey(key);
|
||||||
|
if (containedKey && Objects.equal(value, get(key))) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (force) {
|
||||||
|
inverse().remove(value);
|
||||||
|
} else {
|
||||||
|
checkArgument(!containsValue(value), "value already present: %s", value);
|
||||||
|
}
|
||||||
|
V oldValue = delegate.put(key, value);
|
||||||
|
updateInverseMap(key, containedKey, oldValue, value);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInverseMap(K key, boolean containedKey, V oldValue, V newValue) {
|
||||||
|
if (containedKey) {
|
||||||
|
removeFromInverseMap(oldValue);
|
||||||
|
}
|
||||||
|
inverse.delegate.put(newValue, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object key) {
|
||||||
|
return containsKey(key) ? removeFromBothMaps(key) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private V removeFromBothMaps(Object key) {
|
||||||
|
V oldValue = delegate.remove(key);
|
||||||
|
removeFromInverseMap(oldValue);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFromInverseMap(V oldValue) {
|
||||||
|
inverse.delegate.remove(oldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk Operations
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> map) {
|
||||||
|
for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||||
|
put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
|
||||||
|
this.delegate.replaceAll(function);
|
||||||
|
inverse.delegate.clear();
|
||||||
|
Entry<K, V> broken = null;
|
||||||
|
Iterator<Entry<K, V>> itr = this.delegate.entrySet().iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
Entry<K, V> entry = itr.next();
|
||||||
|
K k = entry.getKey();
|
||||||
|
V v = entry.getValue();
|
||||||
|
K conflict = inverse.delegate.putIfAbsent(v, k);
|
||||||
|
if (conflict != null) {
|
||||||
|
broken = entry;
|
||||||
|
// We're definitely going to throw, but we'll try to keep the BiMap in an internally
|
||||||
|
// consistent state by removing the bad entry.
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (broken != null) {
|
||||||
|
throw new IllegalArgumentException("value already present: " + broken.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
delegate.clear();
|
||||||
|
inverse.delegate.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Views
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiMap<V, K> inverse() {
|
||||||
|
return inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Set<K> keySet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
Set<K> result = keySet;
|
||||||
|
return (result == null) ? keySet = new KeySet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class KeySet extends ForwardingSet<K> {
|
||||||
|
@Override
|
||||||
|
protected Set<K> delegate() {
|
||||||
|
return delegate.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbstractBiMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object key) {
|
||||||
|
if (!contains(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
removeFromBothMaps(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> keysToRemove) {
|
||||||
|
return standardRemoveAll(keysToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> keysToRetain) {
|
||||||
|
return standardRetainAll(keysToRetain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<K> iterator() {
|
||||||
|
return Maps.keyIterator(entrySet().iterator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Set<V> valueSet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> values() {
|
||||||
|
/*
|
||||||
|
* We can almost reuse the inverse's keySet, except we have to fix the
|
||||||
|
* iteration order so that it is consistent with the forward map.
|
||||||
|
*/
|
||||||
|
Set<V> result = valueSet;
|
||||||
|
return (result == null) ? valueSet = new ValueSet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class ValueSet extends ForwardingSet<V> {
|
||||||
|
final Set<V> valuesDelegate = inverse.keySet();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<V> delegate() {
|
||||||
|
return valuesDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return Maps.valueIterator(entrySet().iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return standardToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(T[] array) {
|
||||||
|
return standardToArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return standardToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Set<Entry<K, V>> entrySet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<K, V>> entrySet() {
|
||||||
|
Set<Entry<K, V>> result = entrySet;
|
||||||
|
return (result == null) ? entrySet = new EntrySet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BiMapEntry extends ForwardingMapEntry<K, V> {
|
||||||
|
private final Entry<K, V> delegate;
|
||||||
|
|
||||||
|
BiMapEntry(Entry<K, V> delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Entry<K, V> delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
checkValue(value);
|
||||||
|
// Preconditions keep the map and inverse consistent.
|
||||||
|
checkState(entrySet().contains(this), "entry no longer in map");
|
||||||
|
// similar to putInBothMaps, but set via entry
|
||||||
|
if (Objects.equal(value, getValue())) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
checkArgument(!containsValue(value), "value already present: %s", value);
|
||||||
|
V oldValue = delegate.setValue(value);
|
||||||
|
checkState(Objects.equal(value, get(getKey())), "entry no longer in map");
|
||||||
|
updateInverseMap(getKey(), true, oldValue, value);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Entry<K, V>> entrySetIterator() {
|
||||||
|
final Iterator<Entry<K, V>> iterator = delegate.entrySet().iterator();
|
||||||
|
return new Iterator<Entry<K, V>>() {
|
||||||
|
Entry<K, V> entry;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> next() {
|
||||||
|
entry = iterator.next();
|
||||||
|
return new BiMapEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
checkRemove(entry != null);
|
||||||
|
V value = entry.getValue();
|
||||||
|
iterator.remove();
|
||||||
|
removeFromInverseMap(value);
|
||||||
|
entry = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class EntrySet extends ForwardingSet<Entry<K, V>> {
|
||||||
|
final Set<Entry<K, V>> esDelegate = delegate.entrySet();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<Entry<K, V>> delegate() {
|
||||||
|
return esDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbstractBiMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object object) {
|
||||||
|
if (!esDelegate.contains(object)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// safe because esDelegate.contains(object).
|
||||||
|
Entry<?, ?> entry = (Entry<?, ?>) object;
|
||||||
|
inverse.delegate.remove(entry.getValue());
|
||||||
|
/*
|
||||||
|
* Remove the mapping in inverse before removing from esDelegate because
|
||||||
|
* if entry is part of esDelegate, entry might be invalidated after the
|
||||||
|
* mapping is removed from esDelegate.
|
||||||
|
*/
|
||||||
|
esDelegate.remove(entry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<K, V>> iterator() {
|
||||||
|
return entrySetIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
// See java.util.Collections.CheckedEntrySet for details on attacks.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return standardToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(T[] array) {
|
||||||
|
return standardToArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return Maps.containsEntryImpl(delegate(), o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
return standardContainsAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
return standardRemoveAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
return standardRetainAll(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The inverse of any other {@code AbstractBiMap} subclass. */
|
||||||
|
static class Inverse<K, V> extends AbstractBiMap<K, V> {
|
||||||
|
Inverse(Map<K, V> backward, AbstractBiMap<V, K> forward) {
|
||||||
|
super(backward, forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serialization stores the forward bimap, the inverse of this inverse.
|
||||||
|
* Deserialization calls inverse() on the forward bimap and returns that
|
||||||
|
* inverse.
|
||||||
|
*
|
||||||
|
* If a bimap and its inverse are serialized together, the deserialized
|
||||||
|
* instances have inverse() methods that return the other.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
K checkKey(K key) {
|
||||||
|
return inverse.checkValue(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
V checkValue(V value) {
|
||||||
|
return inverse.checkKey(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @serialData the forward bimap */
|
||||||
|
@GwtIncompatible // java.io.ObjectOutputStream
|
||||||
|
private void writeObject(ObjectOutputStream stream) throws IOException {
|
||||||
|
stream.defaultWriteObject();
|
||||||
|
stream.writeObject(inverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // java.io.ObjectInputStream
|
||||||
|
@SuppressWarnings("unchecked") // reading data stored by writeObject
|
||||||
|
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
|
||||||
|
stream.defaultReadObject();
|
||||||
|
setInverse((AbstractBiMap<V, K>) stream.readObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // Not needed in the emulated source.
|
||||||
|
Object readResolve() {
|
||||||
|
return inverse().inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // Not needed in emulated source.
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // Not needed in emulated source.
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkPositionIndex;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@link ListIterator} interface across a
|
||||||
|
* fixed number of elements that may be retrieved by position. It does not support {@link #remove},
|
||||||
|
* {@link #set}, or {@link #add}.
|
||||||
|
*
|
||||||
|
* @author Jared Levy
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractIndexedListIterator<E> extends UnmodifiableListIterator<E> {
|
||||||
|
private final int size;
|
||||||
|
private int position;
|
||||||
|
|
||||||
|
/** Returns the element with the specified index. This method is called by {@link #next()}. */
|
||||||
|
protected abstract E get(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an iterator across a sequence of the given size whose initial position is 0. That
|
||||||
|
* is, the first call to {@link #next()} will return the first element (or throw {@link
|
||||||
|
* NoSuchElementException} if {@code size} is zero).
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code size} is negative
|
||||||
|
*/
|
||||||
|
protected AbstractIndexedListIterator(int size) {
|
||||||
|
this(size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an iterator across a sequence of the given size with the given initial position.
|
||||||
|
* That is, the first call to {@link #nextIndex()} will return {@code position}, and the first
|
||||||
|
* call to {@link #next()} will return the element at that index, if available. Calls to {@link
|
||||||
|
* #previous()} can retrieve the preceding {@code position} elements.
|
||||||
|
*
|
||||||
|
* @throws IndexOutOfBoundsException if {@code position} is negative or is greater than {@code
|
||||||
|
* size}
|
||||||
|
* @throws IllegalArgumentException if {@code size} is negative
|
||||||
|
*/
|
||||||
|
protected AbstractIndexedListIterator(int size, int position) {
|
||||||
|
checkPositionIndex(position, size);
|
||||||
|
this.size = size;
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean hasNext() {
|
||||||
|
return position < size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final E next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
return get(position++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int nextIndex() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean hasPrevious() {
|
||||||
|
return position > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final E previous() {
|
||||||
|
if (!hasPrevious()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
return get(--position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int previousIndex() {
|
||||||
|
return position - 1;
|
||||||
|
}
|
||||||
|
}
|
174
src/main/java/com/google/common/collect/AbstractIterator.java
Normal file
174
src/main/java/com/google/common/collect/AbstractIterator.java
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@code Iterator} interface, to make this
|
||||||
|
* interface easier to implement for certain types of data sources.
|
||||||
|
*
|
||||||
|
* <p>{@code Iterator} requires its implementations to support querying the end-of-data status
|
||||||
|
* without changing the iterator's state, using the {@link #hasNext} method. But many data sources,
|
||||||
|
* such as {@link java.io.Reader#read()}, do not expose this information; the only way to discover
|
||||||
|
* whether there is any data left is by trying to retrieve it. These types of data sources are
|
||||||
|
* ordinarily difficult to write iterators for. But using this class, one must implement only the
|
||||||
|
* {@link #computeNext} method, and invoke the {@link #endOfData} method when appropriate.
|
||||||
|
*
|
||||||
|
* <p>Another example is an iterator that skips over null elements in a backing iterator. This could
|
||||||
|
* be implemented as:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* public static Iterator<String> skipNulls(final Iterator<String> in) {
|
||||||
|
* return new AbstractIterator<String>() {
|
||||||
|
* protected String computeNext() {
|
||||||
|
* while (in.hasNext()) {
|
||||||
|
* String s = in.next();
|
||||||
|
* if (s != null) {
|
||||||
|
* return s;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* return endOfData();
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>This class supports iterators that include null elements.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
// When making changes to this class, please also update the copy at
|
||||||
|
// com.google.common.base.AbstractIterator
|
||||||
|
@GwtCompatible
|
||||||
|
public abstract class AbstractIterator<T> extends UnmodifiableIterator<T> {
|
||||||
|
private State state = State.NOT_READY;
|
||||||
|
|
||||||
|
/** Constructor for use by subclasses. */
|
||||||
|
protected AbstractIterator() {}
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
/** We have computed the next element and haven't returned it yet. */
|
||||||
|
READY,
|
||||||
|
|
||||||
|
/** We haven't yet computed or have already returned the element. */
|
||||||
|
NOT_READY,
|
||||||
|
|
||||||
|
/** We have reached the end of the data and are finished. */
|
||||||
|
DONE,
|
||||||
|
|
||||||
|
/** We've suffered an exception and are kaput. */
|
||||||
|
FAILED,
|
||||||
|
}
|
||||||
|
|
||||||
|
private T next;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next element. <b>Note:</b> the implementation must call {@link #endOfData()} when
|
||||||
|
* there are no elements left in the iteration. Failure to do so could result in an infinite loop.
|
||||||
|
*
|
||||||
|
* <p>The initial invocation of {@link #hasNext()} or {@link #next()} calls this method, as does
|
||||||
|
* the first invocation of {@code hasNext} or {@code next} following each successful call to
|
||||||
|
* {@code next}. Once the implementation either invokes {@code endOfData} or throws an exception,
|
||||||
|
* {@code computeNext} is guaranteed to never be called again.
|
||||||
|
*
|
||||||
|
* <p>If this method throws an exception, it will propagate outward to the {@code hasNext} or
|
||||||
|
* {@code next} invocation that invoked this method. Any further attempts to use the iterator will
|
||||||
|
* result in an {@link IllegalStateException}.
|
||||||
|
*
|
||||||
|
* <p>The implementation of this method may not invoke the {@code hasNext}, {@code next}, or
|
||||||
|
* {@link #peek()} methods on this instance; if it does, an {@code IllegalStateException} will
|
||||||
|
* result.
|
||||||
|
*
|
||||||
|
* @return the next element if there was one. If {@code endOfData} was called during execution,
|
||||||
|
* the return value will be ignored.
|
||||||
|
* @throws RuntimeException if any unrecoverable error happens. This exception will propagate
|
||||||
|
* outward to the {@code hasNext()}, {@code next()}, or {@code peek()} invocation that invoked
|
||||||
|
* this method. Any further attempts to use the iterator will result in an {@link
|
||||||
|
* IllegalStateException}.
|
||||||
|
*/
|
||||||
|
protected abstract T computeNext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of {@link #computeNext} <b>must</b> invoke this method when there are no
|
||||||
|
* elements left in the iteration.
|
||||||
|
*
|
||||||
|
* @return {@code null}; a convenience so your {@code computeNext} implementation can use the
|
||||||
|
* simple statement {@code return endOfData();}
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected final T endOfData() {
|
||||||
|
state = State.DONE;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(kak): Should we remove this? Some people are using it to prefetch?
|
||||||
|
@Override
|
||||||
|
public final boolean hasNext() {
|
||||||
|
checkState(state != State.FAILED);
|
||||||
|
switch (state) {
|
||||||
|
case DONE:
|
||||||
|
return false;
|
||||||
|
case READY:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return tryToComputeNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryToComputeNext() {
|
||||||
|
state = State.FAILED; // temporary pessimism
|
||||||
|
next = computeNext();
|
||||||
|
if (state != State.DONE) {
|
||||||
|
state = State.READY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(kak): Should we remove this?
|
||||||
|
@Override
|
||||||
|
public final T next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
state = State.NOT_READY;
|
||||||
|
T result = next;
|
||||||
|
next = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next element in the iteration without advancing the iteration, according to the
|
||||||
|
* contract of {@link PeekingIterator#peek()}.
|
||||||
|
*
|
||||||
|
* <p>Implementations of {@code AbstractIterator} that wish to expose this functionality should
|
||||||
|
* implement {@code PeekingIterator}.
|
||||||
|
*/
|
||||||
|
public final T peek() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic implementation of the {@link ListMultimap} interface. It's a wrapper around {@link
|
||||||
|
* AbstractMapBasedMultimap} that converts the returned collections into {@code Lists}. The {@link
|
||||||
|
* #createCollection} method must return a {@code List}.
|
||||||
|
*
|
||||||
|
* @author Jared Levy
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractListMultimap<K, V> extends AbstractMapBasedMultimap<K, V>
|
||||||
|
implements ListMultimap<K, V> {
|
||||||
|
/**
|
||||||
|
* Creates a new multimap that uses the provided map.
|
||||||
|
*
|
||||||
|
* @param map place to store the mapping from each key to its corresponding values
|
||||||
|
*/
|
||||||
|
protected AbstractListMultimap(Map<K, Collection<V>> map) {
|
||||||
|
super(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract List<V> createCollection();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<V> createUnmodifiableEmptyCollection() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<E> Collection<E> unmodifiableCollectionSubclass(Collection<E> collection) {
|
||||||
|
return Collections.unmodifiableList((List<E>) collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Collection<V> wrapCollection(K key, Collection<V> collection) {
|
||||||
|
return wrapList(key, (List<V>) collection, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following Javadoc copied from ListMultimap.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because the values for a given key may have duplicates and follow the insertion ordering,
|
||||||
|
* this method returns a {@link List}, instead of the {@link Collection} specified in the {@link
|
||||||
|
* Multimap} interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<V> get(K key) {
|
||||||
|
return (List<V>) super.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because the values for a given key may have duplicates and follow the insertion ordering,
|
||||||
|
* this method returns a {@link List}, instead of the {@link Collection} specified in the {@link
|
||||||
|
* Multimap} interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<V> removeAll(Object key) {
|
||||||
|
return (List<V>) super.removeAll(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because the values for a given key may have duplicates and follow the insertion ordering,
|
||||||
|
* this method returns a {@link List}, instead of the {@link Collection} specified in the {@link
|
||||||
|
* Multimap} interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<V> replaceValues(K key, Iterable<? extends V> values) {
|
||||||
|
return (List<V>) super.replaceValues(key, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a key-value pair in the multimap.
|
||||||
|
*
|
||||||
|
* @param key key to store in the multimap
|
||||||
|
* @param value value to store in the multimap
|
||||||
|
* @return {@code true} always
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean put(K key, V value) {
|
||||||
|
return super.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Though the method signature doesn't say so explicitly, the returned map has {@link List}
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<K, Collection<V>> asMap() {
|
||||||
|
return super.asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the specified object to this multimap for equality.
|
||||||
|
*
|
||||||
|
* <p>Two {@code ListMultimap} instances are equal if, for each key, they contain the same values
|
||||||
|
* in the same order. If the value orderings disagree, the multimaps will not be considered equal.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
return super.equals(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 6588350623831699109L;
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,338 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.collect.CollectPreconditions.checkNonnegative;
|
||||||
|
import static com.google.common.collect.CollectPreconditions.checkRemove;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.ObjIntConsumer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic implementation of {@code Multiset<E>} backed by an instance of {@code Map<E, Count>}.
|
||||||
|
*
|
||||||
|
* <p>For serialization to work, the subclass must specify explicit {@code readObject} and {@code
|
||||||
|
* writeObject} methods.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> implements Serializable {
|
||||||
|
// TODO(lowasser): consider overhauling this back to Map<E, Integer>
|
||||||
|
private transient Map<E, Count> backingMap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cache the size for efficiency. Using a long lets us avoid the need for
|
||||||
|
* overflow checking and ensures that size() will function correctly even if
|
||||||
|
* the multiset had once been larger than Integer.MAX_VALUE.
|
||||||
|
*/
|
||||||
|
private transient long size;
|
||||||
|
|
||||||
|
/** Standard constructor. */
|
||||||
|
protected AbstractMapBasedMultiset(Map<E, Count> backingMap) {
|
||||||
|
checkArgument(backingMap.isEmpty());
|
||||||
|
this.backingMap = backingMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used during deserialization only. The backing map must be empty. */
|
||||||
|
void setBackingMap(Map<E, Count> backingMap) {
|
||||||
|
this.backingMap = backingMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required Implementations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Invoking {@link Multiset.Entry#getCount} on an entry in the returned set always returns the
|
||||||
|
* current count of that element in the multiset, as opposed to the count at the time the entry
|
||||||
|
* was retrieved.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set<Multiset.Entry<E>> entrySet() {
|
||||||
|
return super.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Iterator<E> elementIterator() {
|
||||||
|
final Iterator<Map.Entry<E, Count>> backingEntries = backingMap.entrySet().iterator();
|
||||||
|
return new Iterator<E>() {
|
||||||
|
Map.Entry<E, Count> toRemove;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return backingEntries.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E next() {
|
||||||
|
final Map.Entry<E, Count> mapEntry = backingEntries.next();
|
||||||
|
toRemove = mapEntry;
|
||||||
|
return mapEntry.getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
checkRemove(toRemove != null);
|
||||||
|
size -= toRemove.getValue().getAndSet(0);
|
||||||
|
backingEntries.remove();
|
||||||
|
toRemove = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Iterator<Entry<E>> entryIterator() {
|
||||||
|
final Iterator<Map.Entry<E, Count>> backingEntries = backingMap.entrySet().iterator();
|
||||||
|
return new Iterator<Multiset.Entry<E>>() {
|
||||||
|
Map.Entry<E, Count> toRemove;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return backingEntries.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Multiset.Entry<E> next() {
|
||||||
|
final Map.Entry<E, Count> mapEntry = backingEntries.next();
|
||||||
|
toRemove = mapEntry;
|
||||||
|
return new Multisets.AbstractEntry<E>() {
|
||||||
|
@Override
|
||||||
|
public E getElement() {
|
||||||
|
return mapEntry.getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
Count count = mapEntry.getValue();
|
||||||
|
if (count == null || count.get() == 0) {
|
||||||
|
Count frequency = backingMap.get(getElement());
|
||||||
|
if (frequency != null) {
|
||||||
|
return frequency.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (count == null) ? 0 : count.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
checkRemove(toRemove != null);
|
||||||
|
size -= toRemove.getValue().getAndSet(0);
|
||||||
|
backingEntries.remove();
|
||||||
|
toRemove = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEachEntry(ObjIntConsumer<? super E> action) {
|
||||||
|
checkNotNull(action);
|
||||||
|
backingMap.forEach((element, count) -> action.accept(element, count.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
for (Count frequency : backingMap.values()) {
|
||||||
|
frequency.set(0);
|
||||||
|
}
|
||||||
|
backingMap.clear();
|
||||||
|
size = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int distinctElements() {
|
||||||
|
return backingMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimizations - Query Operations
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return Ints.saturatedCast(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return new MapBasedMultisetIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not subclassing AbstractMultiset$MultisetIterator because next() needs to
|
||||||
|
* retrieve the Map.Entry<E, Count> entry, which can then be used for
|
||||||
|
* a more efficient remove() call.
|
||||||
|
*/
|
||||||
|
private class MapBasedMultisetIterator implements Iterator<E> {
|
||||||
|
final Iterator<Map.Entry<E, Count>> entryIterator;
|
||||||
|
Map.Entry<E, Count> currentEntry;
|
||||||
|
int occurrencesLeft;
|
||||||
|
boolean canRemove;
|
||||||
|
|
||||||
|
MapBasedMultisetIterator() {
|
||||||
|
this.entryIterator = backingMap.entrySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return occurrencesLeft > 0 || entryIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E next() {
|
||||||
|
if (occurrencesLeft == 0) {
|
||||||
|
currentEntry = entryIterator.next();
|
||||||
|
occurrencesLeft = currentEntry.getValue().get();
|
||||||
|
}
|
||||||
|
occurrencesLeft--;
|
||||||
|
canRemove = true;
|
||||||
|
return currentEntry.getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
checkRemove(canRemove);
|
||||||
|
int frequency = currentEntry.getValue().get();
|
||||||
|
if (frequency <= 0) {
|
||||||
|
throw new ConcurrentModificationException();
|
||||||
|
}
|
||||||
|
if (currentEntry.getValue().addAndGet(-1) == 0) {
|
||||||
|
entryIterator.remove();
|
||||||
|
}
|
||||||
|
size--;
|
||||||
|
canRemove = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int count(Object element) {
|
||||||
|
Count frequency = Maps.safeGet(backingMap, element);
|
||||||
|
return (frequency == null) ? 0 : frequency.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional Operations - Modification Operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the call would result in more than {@link
|
||||||
|
* Integer#MAX_VALUE} occurrences of {@code element} in this multiset.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int add(E element, int occurrences) {
|
||||||
|
if (occurrences == 0) {
|
||||||
|
return count(element);
|
||||||
|
}
|
||||||
|
checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences);
|
||||||
|
Count frequency = backingMap.get(element);
|
||||||
|
int oldCount;
|
||||||
|
if (frequency == null) {
|
||||||
|
oldCount = 0;
|
||||||
|
backingMap.put(element, new Count(occurrences));
|
||||||
|
} else {
|
||||||
|
oldCount = frequency.get();
|
||||||
|
long newCount = (long) oldCount + (long) occurrences;
|
||||||
|
checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount);
|
||||||
|
frequency.add(occurrences);
|
||||||
|
}
|
||||||
|
size += occurrences;
|
||||||
|
return oldCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int remove(Object element, int occurrences) {
|
||||||
|
if (occurrences == 0) {
|
||||||
|
return count(element);
|
||||||
|
}
|
||||||
|
checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences);
|
||||||
|
Count frequency = backingMap.get(element);
|
||||||
|
if (frequency == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldCount = frequency.get();
|
||||||
|
|
||||||
|
int numberRemoved;
|
||||||
|
if (oldCount > occurrences) {
|
||||||
|
numberRemoved = occurrences;
|
||||||
|
} else {
|
||||||
|
numberRemoved = oldCount;
|
||||||
|
backingMap.remove(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
frequency.add(-numberRemoved);
|
||||||
|
size -= numberRemoved;
|
||||||
|
return oldCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roughly a 33% performance improvement over AbstractMultiset.setCount().
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int setCount(E element, int count) {
|
||||||
|
checkNonnegative(count, "count");
|
||||||
|
|
||||||
|
Count existingCounter;
|
||||||
|
int oldCount;
|
||||||
|
if (count == 0) {
|
||||||
|
existingCounter = backingMap.remove(element);
|
||||||
|
oldCount = getAndSet(existingCounter, count);
|
||||||
|
} else {
|
||||||
|
existingCounter = backingMap.get(element);
|
||||||
|
oldCount = getAndSet(existingCounter, count);
|
||||||
|
|
||||||
|
if (existingCounter == null) {
|
||||||
|
backingMap.put(element, new Count(count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size += (count - oldCount);
|
||||||
|
return oldCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getAndSet(Count i, int count) {
|
||||||
|
if (i == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.getAndSet(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow default serialization.
|
||||||
|
@GwtIncompatible // java.io.ObjectStreamException
|
||||||
|
private void readObjectNoData() throws ObjectStreamException {
|
||||||
|
throw new InvalidObjectException("Stream data required");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GwtIncompatible // not needed in emulated source.
|
||||||
|
private static final long serialVersionUID = -2250766705698539974L;
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@code equals}, {@code hashCode}, and {@code toString} methods of {@code
|
||||||
|
* Entry}.
|
||||||
|
*
|
||||||
|
* @author Jared Levy
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractMapEntry<K, V> implements Entry<K, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract K getKey();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract V getValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof Entry) {
|
||||||
|
Entry<?, ?> that = (Entry<?, ?>) object;
|
||||||
|
return Objects.equal(this.getKey(), that.getKey())
|
||||||
|
&& Objects.equal(this.getValue(), that.getValue());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
K k = getKey();
|
||||||
|
V v = getValue();
|
||||||
|
return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a string representation of the form {@code {key}={value}}. */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getKey() + "=" + getValue();
|
||||||
|
}
|
||||||
|
}
|
265
src/main/java/com/google/common/collect/AbstractMultimap.java
Normal file
265
src/main/java/com/google/common/collect/AbstractMultimap.java
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.Spliterators;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractMultimap<K, V> implements Multimap<K, V> {
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
for (Collection<V> collection : asMap().values()) {
|
||||||
|
if (collection.contains(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsEntry(Object key, Object value) {
|
||||||
|
Collection<V> collection = asMap().get(key);
|
||||||
|
return collection != null && collection.contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object key, Object value) {
|
||||||
|
Collection<V> collection = asMap().get(key);
|
||||||
|
return collection != null && collection.remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean put(K key, V value) {
|
||||||
|
return get(key).add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean putAll(K key, Iterable<? extends V> values) {
|
||||||
|
checkNotNull(values);
|
||||||
|
// make sure we only call values.iterator() once
|
||||||
|
// and we only call get(key) if values is nonempty
|
||||||
|
if (values instanceof Collection) {
|
||||||
|
Collection<? extends V> valueCollection = (Collection<? extends V>) values;
|
||||||
|
return !valueCollection.isEmpty() && get(key).addAll(valueCollection);
|
||||||
|
} else {
|
||||||
|
Iterator<? extends V> valueItr = values.iterator();
|
||||||
|
return valueItr.hasNext() && Iterators.addAll(get(key), valueItr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
|
||||||
|
boolean changed = false;
|
||||||
|
for (Entry<? extends K, ? extends V> entry : multimap.entries()) {
|
||||||
|
changed |= put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<V> replaceValues(K key, Iterable<? extends V> values) {
|
||||||
|
checkNotNull(values);
|
||||||
|
Collection<V> result = removeAll(key);
|
||||||
|
putAll(key, values);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Collection<Entry<K, V>> entries;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Entry<K, V>> entries() {
|
||||||
|
Collection<Entry<K, V>> result = entries;
|
||||||
|
return (result == null) ? entries = createEntries() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Collection<Entry<K, V>> createEntries();
|
||||||
|
|
||||||
|
|
||||||
|
class Entries extends Multimaps.Entries<K, V> {
|
||||||
|
@Override
|
||||||
|
Multimap<K, V> multimap() {
|
||||||
|
return AbstractMultimap.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<K, V>> iterator() {
|
||||||
|
return entryIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<Entry<K, V>> spliterator() {
|
||||||
|
return entrySpliterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class EntrySet extends Entries implements Set<Entry<K, V>> {
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Sets.hashCodeImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return Sets.equalsImpl(this, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<Entry<K, V>> entryIterator();
|
||||||
|
|
||||||
|
Spliterator<Entry<K, V>> entrySpliterator() {
|
||||||
|
return Spliterators.spliterator(
|
||||||
|
entryIterator(), size(), (this instanceof SetMultimap) ? Spliterator.DISTINCT : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Set<K> keySet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
Set<K> result = keySet;
|
||||||
|
return (result == null) ? keySet = createKeySet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Set<K> createKeySet();
|
||||||
|
|
||||||
|
private transient Multiset<K> keys;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Multiset<K> keys() {
|
||||||
|
Multiset<K> result = keys;
|
||||||
|
return (result == null) ? keys = createKeys() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Multiset<K> createKeys();
|
||||||
|
|
||||||
|
private transient Collection<V> values;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
Collection<V> result = values;
|
||||||
|
return (result == null) ? values = createValues() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Collection<V> createValues();
|
||||||
|
|
||||||
|
|
||||||
|
class Values extends AbstractCollection<V> {
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return valueIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<V> spliterator() {
|
||||||
|
return valueSpliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbstractMultimap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return AbstractMultimap.this.containsValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbstractMultimap.this.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<V> valueIterator() {
|
||||||
|
return Maps.valueIterator(entries().iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
Spliterator<V> valueSpliterator() {
|
||||||
|
return Spliterators.spliterator(valueIterator(), size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Map<K, Collection<V>> asMap;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<K, Collection<V>> asMap() {
|
||||||
|
Map<K, Collection<V>> result = asMap;
|
||||||
|
return (result == null) ? asMap = createAsMap() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Map<K, Collection<V>> createAsMap();
|
||||||
|
|
||||||
|
// Comparison and hashing
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
return Multimaps.equalsImpl(this, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hash code for this multimap.
|
||||||
|
*
|
||||||
|
* <p>The hash code of a multimap is defined as the hash code of the map view, as returned by
|
||||||
|
* {@link Multimap#asMap}.
|
||||||
|
*
|
||||||
|
* @see Map#hashCode
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return asMap().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the multimap, generated by calling {@code toString} on the
|
||||||
|
* map returned by {@link Multimap#asMap}.
|
||||||
|
*
|
||||||
|
* @return a string representation of the multimap
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return asMap().toString();
|
||||||
|
}
|
||||||
|
}
|
232
src/main/java/com/google/common/collect/AbstractMultiset.java
Normal file
232
src/main/java/com/google/common/collect/AbstractMultiset.java
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Multisets.setCountImpl;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@link Multiset} interface. A new multiset
|
||||||
|
* implementation can be created easily by extending this class and implementing the {@link
|
||||||
|
* Multiset#entrySet()} method, plus optionally overriding {@link #add(Object, int)} and {@link
|
||||||
|
* #remove(Object, int)} to enable modifications to the multiset.
|
||||||
|
*
|
||||||
|
* <p>The {@link #count} and {@link #size} implementations all iterate across the set returned by
|
||||||
|
* {@link Multiset#entrySet()}, as do many methods acting on the set returned by {@link
|
||||||
|
* #elementSet()}. Override those methods for better performance.
|
||||||
|
*
|
||||||
|
* @author Kevin Bourrillion
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractMultiset<E> extends AbstractCollection<E> implements Multiset<E> {
|
||||||
|
// Query Operations
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return entrySet().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object element) {
|
||||||
|
return count(element) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modification Operations
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean add(E element) {
|
||||||
|
add(element, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int add(E element, int occurrences) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean remove(Object element) {
|
||||||
|
return remove(element, 1) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int remove(Object element, int occurrences) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int setCount(E element, int count) {
|
||||||
|
return setCountImpl(this, element, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setCount(E element, int oldCount, int newCount) {
|
||||||
|
return setCountImpl(this, element, oldCount, newCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk Operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This implementation is highly efficient when {@code elementsToAdd} is itself a {@link
|
||||||
|
* Multiset}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean addAll(Collection<? extends E> elementsToAdd) {
|
||||||
|
return Multisets.addAllImpl(this, elementsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean removeAll(Collection<?> elementsToRemove) {
|
||||||
|
return Multisets.removeAllImpl(this, elementsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean retainAll(Collection<?> elementsToRetain) {
|
||||||
|
return Multisets.retainAllImpl(this, elementsToRetain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void clear();
|
||||||
|
|
||||||
|
// Views
|
||||||
|
|
||||||
|
private transient Set<E> elementSet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<E> elementSet() {
|
||||||
|
Set<E> result = elementSet;
|
||||||
|
if (result == null) {
|
||||||
|
elementSet = result = createElementSet();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of this multiset's element set, which will be returned by {@link
|
||||||
|
* #elementSet()}.
|
||||||
|
*/
|
||||||
|
Set<E> createElementSet() {
|
||||||
|
return new ElementSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ElementSet extends Multisets.ElementSet<E> {
|
||||||
|
@Override
|
||||||
|
Multiset<E> multiset() {
|
||||||
|
return AbstractMultiset.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return elementIterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<E> elementIterator();
|
||||||
|
|
||||||
|
private transient Set<Entry<E>> entrySet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<E>> entrySet() {
|
||||||
|
Set<Entry<E>> result = entrySet;
|
||||||
|
if (result == null) {
|
||||||
|
entrySet = result = createEntrySet();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class EntrySet extends Multisets.EntrySet<E> {
|
||||||
|
@Override
|
||||||
|
Multiset<E> multiset() {
|
||||||
|
return AbstractMultiset.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<E>> iterator() {
|
||||||
|
return entryIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return distinctElements();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Entry<E>> createEntrySet() {
|
||||||
|
return new EntrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<Entry<E>> entryIterator();
|
||||||
|
|
||||||
|
abstract int distinctElements();
|
||||||
|
|
||||||
|
// Object methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This implementation returns {@code true} if {@code object} is a multiset of the same size
|
||||||
|
* and if, for each element, the two multisets have the same count.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object object) {
|
||||||
|
return Multisets.equalsImpl(this, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This implementation returns the hash code of {@link Multiset#entrySet()}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return entrySet().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>This implementation returns the result of invoking {@code toString} on {@link
|
||||||
|
* Multiset#entrySet()}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return entrySet().toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
import com.google.common.collect.Maps.IteratorBasedAbstractMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skeletal implementation of {@link NavigableMap}.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
abstract class AbstractNavigableMap<K, V> extends IteratorBasedAbstractMap<K, V>
|
||||||
|
implements NavigableMap<K, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract V get(Object key);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> firstEntry() {
|
||||||
|
return Iterators.getNext(entryIterator(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> lastEntry() {
|
||||||
|
return Iterators.getNext(descendingEntryIterator(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> pollFirstEntry() {
|
||||||
|
return Iterators.pollNext(entryIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> pollLastEntry() {
|
||||||
|
return Iterators.pollNext(descendingEntryIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K firstKey() {
|
||||||
|
Entry<K, V> entry = firstEntry();
|
||||||
|
if (entry == null) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
} else {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K lastKey() {
|
||||||
|
Entry<K, V> entry = lastEntry();
|
||||||
|
if (entry == null) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
} else {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> lowerEntry(K key) {
|
||||||
|
return headMap(key, false).lastEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> floorEntry(K key) {
|
||||||
|
return headMap(key, true).lastEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> ceilingEntry(K key) {
|
||||||
|
return tailMap(key, true).firstEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> higherEntry(K key) {
|
||||||
|
return tailMap(key, false).firstEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K lowerKey(K key) {
|
||||||
|
return Maps.keyOrNull(lowerEntry(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K floorKey(K key) {
|
||||||
|
return Maps.keyOrNull(floorEntry(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K ceilingKey(K key) {
|
||||||
|
return Maps.keyOrNull(ceilingEntry(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K higherKey(K key) {
|
||||||
|
return Maps.keyOrNull(higherEntry(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<Entry<K, V>> descendingEntryIterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedMap<K, V> subMap(K fromKey, K toKey) {
|
||||||
|
return subMap(fromKey, true, toKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedMap<K, V> headMap(K toKey) {
|
||||||
|
return headMap(toKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedMap<K, V> tailMap(K fromKey) {
|
||||||
|
return tailMap(fromKey, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigableSet<K> navigableKeySet() {
|
||||||
|
return new Maps.NavigableKeySet<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
return navigableKeySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigableSet<K> descendingKeySet() {
|
||||||
|
return descendingMap().navigableKeySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigableMap<K, V> descendingMap() {
|
||||||
|
return new DescendingMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DescendingMap extends Maps.DescendingMap<K, V> {
|
||||||
|
@Override
|
||||||
|
NavigableMap<K, V> forward() {
|
||||||
|
return AbstractNavigableMap.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Iterator<Entry<K, V>> entryIterator() {
|
||||||
|
return descendingEntryIterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
src/main/java/com/google/common/collect/AbstractRangeSet.java
Normal file
100
src/main/java/com/google/common/collect/AbstractRangeSet.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the
|
||||||
|
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||||
|
* express or implied. See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtIncompatible;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A skeletal implementation of {@code RangeSet}.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtIncompatible
|
||||||
|
abstract class AbstractRangeSet<C extends Comparable> implements RangeSet<C> {
|
||||||
|
AbstractRangeSet() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(C value) {
|
||||||
|
return rangeContaining(value) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract Range<C> rangeContaining(C value);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return asRanges().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Range<C> range) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Range<C> range) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
remove(Range.<C>all());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean enclosesAll(RangeSet<C> other) {
|
||||||
|
return enclosesAll(other.asRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAll(RangeSet<C> other) {
|
||||||
|
addAll(other.asRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll(RangeSet<C> other) {
|
||||||
|
removeAll(other.asRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(Range<C> otherRange) {
|
||||||
|
return !subRangeSet(otherRange).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract boolean encloses(Range<C> otherRange);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
} else if (obj instanceof RangeSet) {
|
||||||
|
RangeSet<?> other = (RangeSet<?>) obj;
|
||||||
|
return this.asRanges().equals(other.asRanges());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return asRanges().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return asRanges().toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@code Iterator} interface for sequences
|
||||||
|
* whose next element can always be derived from the previous element. Null elements are not
|
||||||
|
* supported, nor is the {@link #remove()} method.
|
||||||
|
*
|
||||||
|
* <p>Example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Iterator<Integer> powersOfTwo =
|
||||||
|
* new AbstractSequentialIterator<Integer>(1) {
|
||||||
|
* protected Integer computeNext(Integer previous) {
|
||||||
|
* return (previous == 1 << 30) ? null : previous * 2;
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Chris Povirk
|
||||||
|
* @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0)
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
public abstract class AbstractSequentialIterator<T> extends UnmodifiableIterator<T> {
|
||||||
|
private T nextOrNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterator with the given first element, or, if {@code firstOrNull} is null,
|
||||||
|
* creates a new empty iterator.
|
||||||
|
*/
|
||||||
|
protected AbstractSequentialIterator(T firstOrNull) {
|
||||||
|
this.nextOrNull = firstOrNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the element that follows {@code previous}, or returns {@code null} if no elements
|
||||||
|
* remain. This method is invoked during each call to {@link #next()} in order to compute the
|
||||||
|
* result of a <i>future</i> call to {@code next()}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected abstract T computeNext(T previous);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean hasNext() {
|
||||||
|
return nextOrNull != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final T next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return nextOrNull;
|
||||||
|
} finally {
|
||||||
|
nextOrNull = computeNext(nextOrNull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
152
src/main/java/com/google/common/collect/AbstractSetMultimap.java
Normal file
152
src/main/java/com/google/common/collect/AbstractSetMultimap.java
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic implementation of the {@link SetMultimap} interface. It's a wrapper around {@link
|
||||||
|
* AbstractMapBasedMultimap} that converts the returned collections into {@code Sets}. The {@link
|
||||||
|
* #createCollection} method must return a {@code Set}.
|
||||||
|
*
|
||||||
|
* @author Jared Levy
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractSetMultimap<K, V> extends AbstractMapBasedMultimap<K, V>
|
||||||
|
implements SetMultimap<K, V> {
|
||||||
|
/**
|
||||||
|
* Creates a new multimap that uses the provided map.
|
||||||
|
*
|
||||||
|
* @param map place to store the mapping from each key to its corresponding values
|
||||||
|
*/
|
||||||
|
protected AbstractSetMultimap(Map<K, Collection<V>> map) {
|
||||||
|
super(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract Set<V> createCollection();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Set<V> createUnmodifiableEmptyCollection() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<E> Collection<E> unmodifiableCollectionSubclass(Collection<E> collection) {
|
||||||
|
return Collections.unmodifiableSet((Set<E>) collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Collection<V> wrapCollection(K key, Collection<V> collection) {
|
||||||
|
return new WrappedSet(key, (Set<V>) collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following Javadoc copied from SetMultimap.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SetMultimap} has unique values for a given key, this method returns a
|
||||||
|
* {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set<V> get(K key) {
|
||||||
|
return (Set<V>) super.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SetMultimap} has unique values for a given key, this method returns a
|
||||||
|
* {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set<Entry<K, V>> entries() {
|
||||||
|
return (Set<Entry<K, V>>) super.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SetMultimap} has unique values for a given key, this method returns a
|
||||||
|
* {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> removeAll(Object key) {
|
||||||
|
return (Set<V>) super.removeAll(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SetMultimap} has unique values for a given key, this method returns a
|
||||||
|
* {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface.
|
||||||
|
*
|
||||||
|
* <p>Any duplicates in {@code values} will be stored in the multimap once.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> replaceValues(K key, Iterable<? extends V> values) {
|
||||||
|
return (Set<V>) super.replaceValues(key, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Though the method signature doesn't say so explicitly, the returned map has {@link Set}
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<K, Collection<V>> asMap() {
|
||||||
|
return super.asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a key-value pair in the multimap.
|
||||||
|
*
|
||||||
|
* @param key key to store in the multimap
|
||||||
|
* @param value value to store in the multimap
|
||||||
|
* @return {@code true} if the method increased the size of the multimap, or {@code false} if the
|
||||||
|
* multimap already contained the key-value pair
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean put(K key, V value) {
|
||||||
|
return super.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the specified object to this multimap for equality.
|
||||||
|
*
|
||||||
|
* <p>Two {@code SetMultimap} instances are equal if, for each key, they contain the same values.
|
||||||
|
* Equality does not depend on the ordering of keys or values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
return super.equals(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 7431625294878419160L;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic implementation of a {@link SortedSetMultimap} with a sorted key set.
|
||||||
|
*
|
||||||
|
* <p>This superclass allows {@code TreeMultimap} to override methods to return navigable set and
|
||||||
|
* map types in non-GWT only, while GWT code will inherit the SortedMap/SortedSet overrides.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractSortedKeySortedSetMultimap<K, V> extends AbstractSortedSetMultimap<K, V> {
|
||||||
|
|
||||||
|
AbstractSortedKeySortedSetMultimap(SortedMap<K, Collection<V>> map) {
|
||||||
|
super(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedMap<K, Collection<V>> asMap() {
|
||||||
|
return (SortedMap<K, Collection<V>>) super.asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SortedMap<K, Collection<V>> backingMap() {
|
||||||
|
return (SortedMap<K, Collection<V>>) super.backingMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedSet<K> keySet() {
|
||||||
|
return (SortedSet<K>) super.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Set<K> createKeySet() {
|
||||||
|
return createMaybeNavigableKeySet();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a skeletal implementation of the {@link SortedMultiset} interface.
|
||||||
|
*
|
||||||
|
* <p>The {@link #count} and {@link #size} implementations all iterate across the set returned by
|
||||||
|
* {@link Multiset#entrySet()}, as do many methods acting on the set returned by {@link
|
||||||
|
* #elementSet()}. Override those methods for better performance.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible(emulated = true)
|
||||||
|
abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements SortedMultiset<E> {
|
||||||
|
@GwtTransient final Comparator<? super E> comparator;
|
||||||
|
|
||||||
|
// needed for serialization
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
AbstractSortedMultiset() {
|
||||||
|
this((Comparator) Ordering.natural());
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSortedMultiset(Comparator<? super E> comparator) {
|
||||||
|
this.comparator = checkNotNull(comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigableSet<E> elementSet() {
|
||||||
|
return (NavigableSet<E>) super.elementSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
NavigableSet<E> createElementSet() {
|
||||||
|
return new SortedMultisets.NavigableElementSet<E>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Comparator<? super E> comparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<E> firstEntry() {
|
||||||
|
Iterator<Entry<E>> entryIterator = entryIterator();
|
||||||
|
return entryIterator.hasNext() ? entryIterator.next() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<E> lastEntry() {
|
||||||
|
Iterator<Entry<E>> entryIterator = descendingEntryIterator();
|
||||||
|
return entryIterator.hasNext() ? entryIterator.next() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<E> pollFirstEntry() {
|
||||||
|
Iterator<Entry<E>> entryIterator = entryIterator();
|
||||||
|
if (entryIterator.hasNext()) {
|
||||||
|
Entry<E> result = entryIterator.next();
|
||||||
|
result = Multisets.immutableEntry(result.getElement(), result.getCount());
|
||||||
|
entryIterator.remove();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<E> pollLastEntry() {
|
||||||
|
Iterator<Entry<E>> entryIterator = descendingEntryIterator();
|
||||||
|
if (entryIterator.hasNext()) {
|
||||||
|
Entry<E> result = entryIterator.next();
|
||||||
|
result = Multisets.immutableEntry(result.getElement(), result.getCount());
|
||||||
|
entryIterator.remove();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedMultiset<E> subMultiset(
|
||||||
|
E fromElement,
|
||||||
|
BoundType fromBoundType,
|
||||||
|
E toElement,
|
||||||
|
BoundType toBoundType) {
|
||||||
|
// These are checked elsewhere, but NullPointerTester wants them checked eagerly.
|
||||||
|
checkNotNull(fromBoundType);
|
||||||
|
checkNotNull(toBoundType);
|
||||||
|
return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<Entry<E>> descendingEntryIterator();
|
||||||
|
|
||||||
|
Iterator<E> descendingIterator() {
|
||||||
|
return Multisets.iteratorImpl(descendingMultiset());
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient SortedMultiset<E> descendingMultiset;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedMultiset<E> descendingMultiset() {
|
||||||
|
SortedMultiset<E> result = descendingMultiset;
|
||||||
|
return (result == null) ? descendingMultiset = createDescendingMultiset() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SortedMultiset<E> createDescendingMultiset() {
|
||||||
|
|
||||||
|
class DescendingMultisetImpl extends DescendingMultiset<E> {
|
||||||
|
@Override
|
||||||
|
SortedMultiset<E> forwardMultiset() {
|
||||||
|
return AbstractSortedMultiset.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Iterator<Entry<E>> entryIterator() {
|
||||||
|
return descendingEntryIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return descendingIterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new DescendingMultisetImpl();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic implementation of the {@link SortedSetMultimap} interface. It's a wrapper around {@link
|
||||||
|
* AbstractMapBasedMultimap} that converts the returned collections into sorted sets. The {@link
|
||||||
|
* #createCollection} method must return a {@code SortedSet}.
|
||||||
|
*
|
||||||
|
* @author Jared Levy
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractSortedSetMultimap<K, V> extends AbstractSetMultimap<K, V>
|
||||||
|
implements SortedSetMultimap<K, V> {
|
||||||
|
/**
|
||||||
|
* Creates a new multimap that uses the provided map.
|
||||||
|
*
|
||||||
|
* @param map place to store the mapping from each key to its corresponding values
|
||||||
|
*/
|
||||||
|
protected AbstractSortedSetMultimap(Map<K, Collection<V>> map) {
|
||||||
|
super(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
abstract SortedSet<V> createCollection();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SortedSet<V> createUnmodifiableEmptyCollection() {
|
||||||
|
return unmodifiableCollectionSubclass(createCollection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<E> SortedSet<E> unmodifiableCollectionSubclass(Collection<E> collection) {
|
||||||
|
if (collection instanceof NavigableSet) {
|
||||||
|
return Sets.unmodifiableNavigableSet((NavigableSet<E>) collection);
|
||||||
|
} else {
|
||||||
|
return Collections.unmodifiableSortedSet((SortedSet<E>) collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Collection<V> wrapCollection(K key, Collection<V> collection) {
|
||||||
|
if (collection instanceof NavigableSet) {
|
||||||
|
return new WrappedNavigableSet(key, (NavigableSet<V>) collection, null);
|
||||||
|
} else {
|
||||||
|
return new WrappedSortedSet(key, (SortedSet<V>) collection, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following Javadoc copied from Multimap and SortedSetMultimap.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection view of all values associated with a key. If no mappings in the multimap
|
||||||
|
* have the provided key, an empty collection is returned.
|
||||||
|
*
|
||||||
|
* <p>Changes to the returned collection will update the underlying multimap, and vice versa.
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method
|
||||||
|
* returns a {@link SortedSet}, instead of the {@link Collection} specified in the {@link
|
||||||
|
* Multimap} interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SortedSet<V> get(K key) {
|
||||||
|
return (SortedSet<V>) super.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all values associated with a given key. The returned collection is immutable.
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method
|
||||||
|
* returns a {@link SortedSet}, instead of the {@link Collection} specified in the {@link
|
||||||
|
* Multimap} interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedSet<V> removeAll(Object key) {
|
||||||
|
return (SortedSet<V>) super.removeAll(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a collection of values with the same key, replacing any existing values for that key.
|
||||||
|
* The returned collection is immutable.
|
||||||
|
*
|
||||||
|
* <p>Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method
|
||||||
|
* returns a {@link SortedSet}, instead of the {@link Collection} specified in the {@link
|
||||||
|
* Multimap} interface.
|
||||||
|
*
|
||||||
|
* <p>Any duplicates in {@code values} will be stored in the multimap once.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedSet<V> replaceValues(K key, Iterable<? extends V> values) {
|
||||||
|
return (SortedSet<V>) super.replaceValues(key, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map view that associates each key with the corresponding values in the multimap.
|
||||||
|
* Changes to the returned map, such as element removal, will update the underlying multimap. The
|
||||||
|
* map does not support {@code setValue} on its entries, {@code put}, or {@code putAll}.
|
||||||
|
*
|
||||||
|
* <p>When passed a key that is present in the map, {@code asMap().get(Object)} has the same
|
||||||
|
* behavior as {@link #get}, returning a live collection. When passed a key that is not present,
|
||||||
|
* however, {@code asMap().get(Object)} returns {@code null} instead of an empty collection.
|
||||||
|
*
|
||||||
|
* <p>Though the method signature doesn't say so explicitly, the returned map has {@link
|
||||||
|
* SortedSet} values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<K, Collection<V>> asMap() {
|
||||||
|
return super.asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>Consequently, the values do not follow their natural ordering or the ordering of the value
|
||||||
|
* comparator.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
return super.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 430848587173315748L;
|
||||||
|
}
|
241
src/main/java/com/google/common/collect/AbstractTable.java
Normal file
241
src/main/java/com/google/common/collect/AbstractTable.java
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skeletal, implementation-agnostic implementation of the {@link Table} interface.
|
||||||
|
*
|
||||||
|
* @author Louis Wasserman
|
||||||
|
*/
|
||||||
|
@GwtCompatible
|
||||||
|
abstract class AbstractTable<R, C, V> implements Table<R, C, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsRow(Object rowKey) {
|
||||||
|
return Maps.safeContainsKey(rowMap(), rowKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsColumn(Object columnKey) {
|
||||||
|
return Maps.safeContainsKey(columnMap(), columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<R> rowKeySet() {
|
||||||
|
return rowMap().keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<C> columnKeySet() {
|
||||||
|
return columnMap().keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
for (Map<C, V> row : rowMap().values()) {
|
||||||
|
if (row.containsValue(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object rowKey, Object columnKey) {
|
||||||
|
Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
|
||||||
|
return row != null && Maps.safeContainsKey(row, columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object rowKey, Object columnKey) {
|
||||||
|
Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
|
||||||
|
return (row == null) ? null : Maps.safeGet(row, columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
Iterators.clear(cellSet().iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object rowKey, Object columnKey) {
|
||||||
|
Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
|
||||||
|
return (row == null) ? null : Maps.safeRemove(row, columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(R rowKey, C columnKey, V value) {
|
||||||
|
return row(rowKey).put(columnKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
|
||||||
|
for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
|
||||||
|
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Set<Cell<R, C, V>> cellSet;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Cell<R, C, V>> cellSet() {
|
||||||
|
Set<Cell<R, C, V>> result = cellSet;
|
||||||
|
return (result == null) ? cellSet = createCellSet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Cell<R, C, V>> createCellSet() {
|
||||||
|
return new CellSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<Table.Cell<R, C, V>> cellIterator();
|
||||||
|
|
||||||
|
abstract Spliterator<Table.Cell<R, C, V>> cellSpliterator();
|
||||||
|
|
||||||
|
|
||||||
|
class CellSet extends AbstractSet<Cell<R, C, V>> {
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
if (o instanceof Cell) {
|
||||||
|
Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
|
||||||
|
Map<C, V> row = Maps.safeGet(rowMap(), cell.getRowKey());
|
||||||
|
return row != null
|
||||||
|
&& Collections2.safeContains(
|
||||||
|
row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
if (o instanceof Cell) {
|
||||||
|
Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
|
||||||
|
Map<C, V> row = Maps.safeGet(rowMap(), cell.getRowKey());
|
||||||
|
return row != null
|
||||||
|
&& Collections2.safeRemove(
|
||||||
|
row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbstractTable.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Table.Cell<R, C, V>> iterator() {
|
||||||
|
return cellIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<Cell<R, C, V>> spliterator() {
|
||||||
|
return cellSpliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbstractTable.this.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient Collection<V> values;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
Collection<V> result = values;
|
||||||
|
return (result == null) ? values = createValues() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<V> createValues() {
|
||||||
|
return new Values();
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<V> valuesIterator() {
|
||||||
|
return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) {
|
||||||
|
@Override
|
||||||
|
V transform(Cell<R, C, V> cell) {
|
||||||
|
return cell.getValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Spliterator<V> valuesSpliterator() {
|
||||||
|
return CollectSpliterators.map(cellSpliterator(), Table.Cell::getValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Values extends AbstractCollection<V> {
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return valuesIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<V> spliterator() {
|
||||||
|
return valuesSpliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return containsValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbstractTable.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbstractTable.this.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return Tables.equalsImpl(this, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return cellSet().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the string representation {@code rowMap().toString()}. */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return rowMap().toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Guava Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.common.collect;
|
||||||
|
|
||||||
|
import com.google.common.annotations.GwtCompatible;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ordering that treats all references as equals, even nulls.
|
||||||
|
*
|
||||||
|
* @author Emily Soldal
|
||||||
|
*/
|
||||||
|
@GwtCompatible(serializable = true)
|
||||||
|
final class AllEqualOrdering extends Ordering<Object> implements Serializable {
|
||||||
|
static final AllEqualOrdering INSTANCE = new AllEqualOrdering();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Object left, Object right) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E> List<E> sortedCopy(Iterable<E> iterable) {
|
||||||
|
return Lists.newArrayList(iterable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E> ImmutableList<E> immutableSortedCopy(Iterable<E> iterable) {
|
||||||
|
return ImmutableList.copyOf(iterable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <S> Ordering<S> reverse() {
|
||||||
|
return (Ordering<S>) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResolve() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Ordering.allEqual()";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue