Compare commits

..

No commits in common. "main" and "29.0" have entirely different histories.
main ... 29.0

108 changed files with 1796 additions and 16039 deletions

3
.travis.yml Normal file
View file

@ -0,0 +1,3 @@
language: java
jdk:
- openjdk11

View file

@ -1,15 +1,15 @@
# Guava - simplified and reduced # xbib Guava
This is xbib Guava, a build of Google Guava with the following differences to the This is xbib Guava, a build of Google Guava with the following differences to the
original [Google Guava Library](https://github.com/google/guava): original [Google Guava Library](https://github.com/google/guava):
- forked master branch on November 21, 2019 (28.1+) - forked master branch on November 21, 2019 ("28.1+")
- removed all external annotations, so this library does not have any dependencies - removed all external annotations, so this library does not have any dependencies
- compiled under Java 11 and with a module info for JPMS (module org.xbib.guava) - removed duplicate JDK classes (LongAdder, Striped64)
- removed duplicate JDK11 classes (LongAdder, Striped64)
- replaced sun.misc.Unsafe dependent classes with safe versions (LongAdders, UnsignedBytes, LittleEndianByteArray, AbstractFuture) - replaced sun.misc.Unsafe dependent classes with safe versions (LongAdders, UnsignedBytes, LittleEndianByteArray, AbstractFuture)
- the guava failureaccess dependency is included - the guava failureaccess dependency is included
- removed listenablefuture empty dependency hack - 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 - Gradle as build system
All credits belong to the original authors All credits belong to the original authors

View file

@ -1,16 +1,15 @@
plugins { plugins {
id 'maven-publish' id "de.marcphilipp.nexus-publish" version "0.4.0"
id 'signing' id "io.codearte.nexus-staging" version "0.21.1"
id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1"
} }
wrapper { wrapper {
gradleVersion = libs.versions.gradle.get() gradleVersion = "${project.property('gradle.wrapper.version')}"
distributionType = Wrapper.DistributionType.ALL distributionType = Wrapper.DistributionType.ALL
} }
ext { ext {
user = 'joerg' user = 'jprante'
name = 'guava' name = 'guava'
description = 'Guava implementation with named modules for Java 11+' description = 'Guava implementation with named modules for Java 11+'
inceptionYear = '2019' inceptionYear = '2019'
@ -25,10 +24,8 @@ ext {
} }
apply plugin: 'java-library' apply plugin: 'java-library'
apply from: rootProject.file('gradle/ide/idea.gradle')
apply from: rootProject.file('gradle/repositories/maven.gradle')
apply from: rootProject.file('gradle/compile/java.gradle') apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle') apply from: rootProject.file('gradle/test/junit5.gradle')
apply from: rootProject.file('gradle/publish/maven.gradle') apply from: rootProject.file('gradle/publishing/publication.gradle')
apply from: rootProject.file('gradle/publish/sonatype.gradle') apply from: rootProject.file('gradle/publishing/sonatype.gradle')
apply from: rootProject.file('gradle/publish/forgejo.gradle')

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

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

View file

@ -1,3 +1,5 @@
group = org.xbib group = org.xbib
name = guava name = guava
version = 30.2 version = 29.0
gradle.wrapper.version = 6.6.1

View file

@ -2,29 +2,36 @@
apply plugin: 'java-library' apply plugin: 'java-library'
java { java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
modularity.inferModulePath.set(true) modularity.inferModulePath.set(true)
withSourcesJar() }
withJavadocJar()
compileJava {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
jar { jar {
manifest { manifest {
attributes('Implementation-Title': project.name)
attributes('Implementation-Version': project.version) attributes('Implementation-Version': project.version)
attributes('Implementation-Vendor': 'Jörg Prante')
} }
} }
tasks.withType(JavaCompile) { task sourcesJar(type: Jar, dependsOn: classes) {
options.fork = true classifier 'sources'
options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US'] from sourceSets.main.allSource
options.compilerArgs.add('-Xlint:-requires-transitive-automatic')
options.compilerArgs.add('-Xlint:deprecation')
options.encoding = 'UTF-8'
} }
tasks.withType(Javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) {
options.addStringOption('Xdoclint:none', '-quiet') classifier 'javadoc'
options.encoding = 'UTF-8' }
artifacts {
archives sourcesJar, javadocJar
} }

View file

@ -7,6 +7,7 @@ idea {
} }
} }
// if project was not imported via idea pluginw
clean { clean {
delete 'out' delete 'out'
} }

View file

@ -1,14 +0,0 @@
gradle.projectsLoaded { g ->
printf "Host: %s\nOS: %s %s %s\nJVM: %s %s %s %s\nGradle: %s Groovy: %s Java: %s\n" +
"Build: group: %s name: %s version: %s\n",
InetAddress.getLocalHost(),
System.getProperty("os.name"),
System.getProperty("os.arch"),
System.getProperty("os.version"),
System.getProperty("java.version"),
System.getProperty("java.vm.version"),
System.getProperty("java.vm.vendor"),
System.getProperty("java.vm.name"),
gradle.gradleVersion, GroovySystem.getVersion(), JavaVersion.current(),
gradle.rootProject.group, gradle.rootProject.name, gradle.rootProject.version
}

View file

@ -1,16 +0,0 @@
if (project.hasProperty('forgeJoToken')) {
publishing {
repositories {
maven {
url 'https://xbib.org/api/packages/joerg/maven'
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = "token ${project.property('forgeJoToken')}"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}
}

View file

@ -1,27 +0,0 @@
apply plugin: 'ivy-publish'
publishing {
repositories {
ivy {
url = "https://xbib.org/repo"
}
}
publications {
ivy(IvyPublication) {
from components.java
descriptor {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
author {
name = 'Jörg Prante'
url = 'https://xbib.org/joerg'
}
descriptor.description {
text = rootProject.ext.description
}
}
}
}
}

View file

@ -1,11 +0,0 @@
if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) {
nexusPublishing {
repositories {
sonatype {
username = project.property('ossrhUsername')
password = project.property('ossrhPassword')
packageGroup = "org.xbib"
}
}
}
}

View file

@ -1,10 +1,13 @@
apply plugin: "de.marcphilipp.nexus-publish"
publishing { publishing {
publications { publications {
"${project.name}"(MavenPublication) { mavenJava(MavenPublication) {
from components.java from components.java
artifact sourcesJar
artifact javadocJar
pom { pom {
artifactId = project.name
name = project.name name = project.name
description = rootProject.ext.description description = rootProject.ext.description
url = rootProject.ext.url url = rootProject.ext.url
@ -16,10 +19,10 @@ publishing {
} }
developers { developers {
developer { developer {
id = 'joerg' id = 'jprante'
name = 'Jörg Prante' name = 'Jörg Prante'
email = 'joergprante@gmail.com' email = 'joergprante@gmail.com'
url = 'https://xbib.org/joerg' url = 'https://github.com/jprante'
} }
} }
scm { scm {
@ -46,6 +49,16 @@ publishing {
if (project.hasProperty("signing.keyId")) { if (project.hasProperty("signing.keyId")) {
apply plugin: 'signing' apply plugin: 'signing'
signing { signing {
sign publishing.publications."${project.name}" sign publishing.publications.mavenJava
}
}
nexusPublishing {
repositories {
sonatype {
username = project.property('ossrhUsername')
password = project.property('ossrhPassword')
packageGroup = "org.xbib"
}
} }
} }

View file

@ -0,0 +1,11 @@
if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) {
apply plugin: 'io.codearte.nexus-staging'
nexusStaging {
username = project.property('ossrhUsername')
password = project.property('ossrhPassword')
packageGroup = "org.xbib"
}
}

View file

@ -1,18 +0,0 @@
tasks.withType(Checkstyle) {
ignoreFailures = true
reports {
xml.getRequired().set(true)
html.getRequired().set(true)
}
}
checkstyle {
toolVersion = '10.4'
configFile = rootProject.file('gradle/quality/checkstyle.xml')
ignoreFailures = true
showViolations = false
checkstyleMain {
source = sourceSets.main.allSource
}
}

View file

@ -1,333 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!-- This is a checkstyle configuration file. For descriptions of
what the following rules do, please see the checkstyle configuration
page at http://checkstyle.sourceforge.net/config.html -->
<module name="Checker">
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value=".*(Example|Test|module-info)(\$.*)?"/>
</module>
<module name="FileTabCharacter">
<!-- Checks that there are no tab characters in the file.
-->
</module>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
</module>
<module name="RegexpSingleline">
<!-- Checks that FIXME is not used in comments. TODO is preferred.
-->
<property name="format" value="((//.*)|(\*.*))FIXME" />
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
</module>
<module name="RegexpSingleline">
<!-- Checks that TODOs are named. (Actually, just that they are followed
by an open paren.)
-->
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
</module>
<module name="JavadocPackage">
<!-- Checks that each Java package has a Javadoc file used for commenting.
Only allows a package-info.java, not package.html. -->
</module>
<!-- All Java AST specific tests live under TreeWalker module. -->
<module name="TreeWalker">
<!--
IMPORT CHECKS
-->
<module name="RedundantImport">
<!-- Checks for redundant import statements. -->
<property name="severity" value="error"/>
</module>
<module name="ImportOrder">
<!-- Checks for out of order import statements. -->
<property name="severity" value="warning"/>
<!-- <property name="tokens" value="IMPORT, STATIC_IMPORT"/> -->
<property name="separated" value="false"/>
<property name="groups" value="*"/>
<!-- <property name="option" value="above"/> -->
<property name="sortStaticImportsAlphabetically" value="true"/>
</module>
<module name="CustomImportOrder">
<!-- <property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> -->
<!-- <property name="specialImportsRegExp" value="^javax\."/> -->
<!-- <property name="standardPackageRegExp" value="^java\."/> -->
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="false"/>
</module>
<!--
JAVADOC CHECKS
-->
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<module name="JavadocMethod">
<property name="accessModifiers" value="protected"/>
<property name="severity" value="warning"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
</module>
<module name="JavadocType">
<property name="scope" value="protected"/>
<property name="severity" value="error"/>
</module>
<module name="JavadocStyle">
<property name="severity" value="warning"/>
</module>
<!--
NAMING CHECKS
-->
<!-- Item 38 - Adhere to generally accepted naming conventions -->
<module name="PackageName">
<!-- Validates identifiers for package names against the
supplied expression. -->
<!-- Here the default checkstyle rule restricts package name parts to
seven characters, this is not in line with common practice at Google.
-->
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
<property name="severity" value="warning"/>
</module>
<module name="TypeNameCheck">
<!-- Validates static, final fields against the
expression "^[A-Z][a-zA-Z0-9]*$". -->
<metadata name="altname" value="TypeName"/>
<property name="severity" value="warning"/>
</module>
<module name="ConstantNameCheck">
<!-- Validates non-private, static, final fields against the supplied
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
<metadata name="altname" value="ConstantName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="false"/>
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
<message key="name.invalidPattern"
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
<property name="severity" value="warning"/>
</module>
<module name="StaticVariableNameCheck">
<!-- Validates static, non-final fields against the supplied
expression "^[a-z][a-zA-Z0-9]*_?$". -->
<metadata name="altname" value="StaticVariableName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="true"/>
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
<property name="severity" value="warning"/>
</module>
<module name="MemberNameCheck">
<!-- Validates non-static members against the supplied expression. -->
<metadata name="altname" value="MemberName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="true"/>
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
<property name="severity" value="warning"/>
</module>
<module name="MethodNameCheck">
<!-- Validates identifiers for method names. -->
<metadata name="altname" value="MethodName"/>
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
<property name="severity" value="warning"/>
</module>
<module name="ParameterName">
<!-- Validates identifiers for method parameters against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<module name="LocalFinalVariableName">
<!-- Validates identifiers for local final variables against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<module name="LocalVariableName">
<!-- Validates identifiers for local variables against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<!--
LENGTH and CODING CHECKS
-->
<module name="LeftCurly">
<!-- Checks for placement of the left curly brace ('{'). -->
<property name="severity" value="warning"/>
</module>
<module name="RightCurly">
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
the same line. e.g., the following example is fine:
<pre>
if {
...
} else
</pre>
-->
<!-- This next example is not fine:
<pre>
if {
...
}
else
</pre>
-->
<property name="option" value="same"/>
<property name="severity" value="warning"/>
</module>
<!-- Checks for braces around if and else blocks -->
<module name="NeedBraces">
<property name="severity" value="warning"/>
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
</module>
<module name="UpperEll">
<!-- Checks that long constants are defined with an upper ell.-->
<property name="severity" value="error"/>
</module>
<module name="FallThrough">
<!-- Warn about falling through to the next case statement. Similar to
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
some other variants which we don't publicized to promote consistency).
-->
<property name="reliefPattern"
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
<property name="severity" value="error"/>
</module>
<!--
MODIFIERS CHECKS
-->
<module name="ModifierOrder">
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
8.4.3. The prescribed order is:
public, protected, private, abstract, static, final, transient, volatile,
synchronized, native, strictfp
-->
</module>
<!--
WHITESPACE CHECKS
-->
<module name="WhitespaceAround">
<!-- Checks that various tokens are surrounded by whitespace.
This includes most binary operators and keywords followed
by regular or curly braces.
-->
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
<property name="severity" value="error"/>
</module>
<module name="WhitespaceAfter">
<!-- Checks that commas, semicolons and typecasts are followed by
whitespace.
-->
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
</module>
<module name="NoWhitespaceAfter">
<!-- Checks that there is no whitespace after various unary operators.
Linebreaks are allowed.
-->
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
UNARY_PLUS"/>
<property name="allowLineBreaks" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="NoWhitespaceBefore">
<!-- Checks that there is no whitespace before various unary operators.
Linebreaks are allowed.
-->
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
<property name="allowLineBreaks" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="ParenPad">
<!-- Checks that there is no whitespace before close parens or after
open parens.
-->
<property name="severity" value="warning"/>
</module>
</module>
<module name="LineLength">
<!-- Checks if a line is too long. -->
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
<property name="severity" value="error"/>
<!--
The default ignore pattern exempts the following elements:
- import statements
- long URLs inside comments
-->
<property name="ignorePattern"
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
</module>
</module>

View file

@ -1,11 +0,0 @@
cyclonedxBom {
includeConfigs = [ 'runtimeClasspath' ]
skipConfigs = [ 'compileClasspath', 'testCompileClasspath' ]
projectType = "library"
schemaVersion = "1.4"
destination = file("build/reports")
outputName = "bom"
outputFormat = "json"
includeBomSerialNumber = true
componentVersion = "2.0.0"
}

View file

@ -1,15 +0,0 @@
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.getRequired().set(true)
html.getRequired().set(true)
}
}
pmd {
ignoreFailures = true
consoleOutput = false
toolVersion = "6.51.0"
ruleSetFiles = rootProject.files('gradle/quality/pmd/category/java/bestpractices.xml')
}

File diff suppressed because it is too large Load diff

View file

@ -1,10 +0,0 @@
rulesets.filenames=\
category/java/bestpractices.xml,\
category/java/codestyle.xml,\
category/java/design.xml,\
category/java/documentation.xml,\
category/java/errorprone.xml,\
category/java/multithreading.xml,\
category/java/performance.xml,\
category/java/security.xml

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,144 +0,0 @@
<?xml version="1.0"?>
<ruleset name="Documentation"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Rules that are related to code documentation.
</description>
<rule name="CommentContent"
since="5.0"
message="Invalid words or phrases found"
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentContentRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentcontent">
<description>
A rule for the politically correct... we don't want to offend anyone.
</description>
<priority>3</priority>
<example>
<![CDATA[
//OMG, this is horrible, Bob is an idiot !!!
]]>
</example>
</rule>
<rule name="CommentRequired"
since="5.1"
message="Comment is required"
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentRequiredRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentrequired">
<description>
Denotes whether comments are required (or unwanted) for specific language elements.
</description>
<priority>3</priority>
<example>
<![CDATA[
/**
*
*
* @author Jon Doe
*/
]]>
</example>
</rule>
<rule name="CommentSize"
since="5.0"
message="Comment is too large"
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentSizeRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentsize">
<description>
Determines whether the dimensions of non-header comments found are within the specified limits.
</description>
<priority>3</priority>
<example>
<![CDATA[
/**
*
* too many lines!
*
*
*
*
*
*
*
*
*
*
*
*
*/
]]>
</example>
</rule>
<rule name="UncommentedEmptyConstructor"
language="java"
since="3.4"
message="Document empty constructor"
class="net.sourceforge.pmd.lang.rule.XPathRule"
typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#uncommentedemptyconstructor">
<description>
Uncommented Empty Constructor finds instances where a constructor does not
contain statements, but there is no comment. By explicitly commenting empty
constructors it is easier to distinguish between intentional (commented)
and unintentional empty constructors.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//ConstructorDeclaration[@Private='false']
[count(BlockStatement) = 0 and ($ignoreExplicitConstructorInvocation = 'true' or not(ExplicitConstructorInvocation)) and @containsComment = 'false']
[not(../Annotation/MarkerAnnotation/Name[pmd-java:typeIs('javax.inject.Inject')])]
]]>
</value>
</property>
<property name="ignoreExplicitConstructorInvocation" type="Boolean" description="Ignore explicit constructor invocation when deciding whether constructor is empty or not" value="false"/>
</properties>
<example>
<![CDATA[
public Foo() {
// This constructor is intentionally empty. Nothing special is needed here.
}
]]>
</example>
</rule>
<rule name="UncommentedEmptyMethodBody"
language="java"
since="3.4"
message="Document empty method body"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#uncommentedemptymethodbody">
<description>
Uncommented Empty Method Body finds instances where a method body does not contain
statements, but there is no comment. By explicitly commenting empty method bodies
it is easier to distinguish between intentional (commented) and unintentional
empty methods.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//MethodDeclaration/Block[count(BlockStatement) = 0 and @containsComment = 'false']
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
public void doSomething() {
}
]]>
</example>
</rule>
</ruleset>

File diff suppressed because it is too large Load diff

View file

@ -1,393 +0,0 @@
<?xml version="1.0"?>
<ruleset name="Multithreading"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Rules that flag issues when dealing with multiple threads of execution.
</description>
<rule name="AvoidSynchronizedAtMethodLevel"
language="java"
since="3.0"
message="Use block level rather than method level synchronization"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidsynchronizedatmethodlevel">
<description>
Method-level synchronization can cause problems when new code is added to the method.
Block-level synchronization helps to ensure that only the code that needs synchronization
gets it.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>//MethodDeclaration[@Synchronized='true']</value>
</property>
</properties>
<example>
<![CDATA[
public class Foo {
// Try to avoid this:
synchronized void foo() {
}
// Prefer this:
void bar() {
synchronized(this) {
}
}
// Try to avoid this for static methods:
static synchronized void fooStatic() {
}
// Prefer this:
static void barStatic() {
synchronized(Foo.class) {
}
}
}
]]>
</example>
</rule>
<rule name="AvoidThreadGroup"
language="java"
since="3.6"
message="Avoid using java.lang.ThreadGroup; it is not thread safe"
class="net.sourceforge.pmd.lang.rule.XPathRule"
typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidthreadgroup">
<description>
Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environment
it contains methods that are not thread-safe.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.ThreadGroup')]|
//PrimarySuffix[contains(@Image, 'getThreadGroup')]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
public class Bar {
void buz() {
ThreadGroup tg = new ThreadGroup("My threadgroup");
tg = new ThreadGroup(tg, "my thread group");
tg = Thread.currentThread().getThreadGroup();
tg = System.getSecurityManager().getThreadGroup();
}
}
]]>
</example>
</rule>
<rule name="AvoidUsingVolatile"
language="java"
since="4.1"
class="net.sourceforge.pmd.lang.rule.XPathRule"
message="Use of modifier volatile is not recommended."
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidusingvolatile">
<description>
Use of the keyword 'volatile' is generally used to fine tune a Java application, and therefore, requires
a good expertise of the Java Memory Model. Moreover, its range of action is somewhat misknown. Therefore,
the volatile keyword should not be used for maintenance purpose and portability.
</description>
<priority>2</priority>
<properties>
<property name="xpath">
<value>//FieldDeclaration[contains(@Volatile,'true')]</value>
</property>
</properties>
<example>
<![CDATA[
public class ThrDeux {
private volatile String var1; // not suggested
private String var2; // preferred
}
]]>
</example>
</rule>
<rule name="DoNotUseThreads"
language="java"
since="4.1"
class="net.sourceforge.pmd.lang.rule.XPathRule"
message="To be compliant to J2EE, a webapp should not use any thread."
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#donotusethreads">
<description>
The J2EE specification explicitly forbids the use of threads.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>//ClassOrInterfaceType[@Image = 'Thread' or @Image = 'Runnable']</value>
</property>
</properties>
<example>
<![CDATA[
// This is not allowed
public class UsingThread extends Thread {
}
// Neither this,
public class OtherThread implements Runnable {
// Nor this ...
public void methode() {
Runnable thread = new Thread(); thread.run();
}
}
]]>
</example>
</rule>
<rule name="DontCallThreadRun"
language="java"
since="4.3"
message="Don't call Thread.run() explicitly, use Thread.start()"
class="net.sourceforge.pmd.lang.rule.XPathRule"
typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#dontcallthreadrun">
<description>
Explicitly calling Thread.run() method will execute in the caller's thread of control. Instead, call Thread.start() for the intended behavior.
</description>
<priority>4</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//StatementExpression/PrimaryExpression
[
PrimaryPrefix
[
./Name[ends-with(@Image, '.run') or @Image = 'run']
and substring-before(Name/@Image, '.') =//VariableDeclarator/VariableDeclaratorId/@Image
[../../../Type/ReferenceType/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]]
or (./AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]
and ../PrimarySuffix[@Image = 'run'])
]
]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
Thread t = new Thread();
t.run(); // use t.start() instead
new Thread().run(); // same violation
]]>
</example>
</rule>
<rule name="DoubleCheckedLocking"
language="java"
since="1.04"
message="Double checked locking is not thread safe in Java."
class="net.sourceforge.pmd.lang.java.rule.multithreading.DoubleCheckedLockingRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#doublecheckedlocking">
<description>
Partially created objects can be returned by the Double Checked Locking pattern when used in Java.
An optimizing JRE may assign a reference to the baz variable before it calls the constructor of the object the
reference points to.
Note: With Java 5, you can make Double checked locking work, if you declare the variable to be `volatile`.
For more details refer to: &lt;http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html>
or &lt;http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html>
</description>
<priority>1</priority>
<example>
<![CDATA[
public class Foo {
/*volatile */ Object baz = null; // fix for Java5 and later: volatile
Object bar() {
if (baz == null) { // baz may be non-null yet not fully created
synchronized(this) {
if (baz == null) {
baz = new Object();
}
}
}
return baz;
}
}
]]>
</example>
</rule>
<rule name="NonThreadSafeSingleton"
since="3.4"
message="Singleton is not thread safe"
class="net.sourceforge.pmd.lang.java.rule.multithreading.NonThreadSafeSingletonRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#nonthreadsafesingleton">
<description>
Non-thread safe singletons can result in bad state changes. Eliminate
static singletons if possible by instantiating the object directly. Static
singletons are usually not needed as only a single instance exists anyway.
Other possible fixes are to synchronize the entire method or to use an
[initialize-on-demand holder class](https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom).
Refrain from using the double-checked locking pattern. The Java Memory Model doesn't
guarantee it to work unless the variable is declared as `volatile`, adding an uneeded
performance penalty. [Reference](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)
See Effective Java, item 48.
</description>
<priority>3</priority>
<example>
<![CDATA[
private static Foo foo = null;
//multiple simultaneous callers may see partially initialized objects
public static Foo getFoo() {
if (foo==null) {
foo = new Foo();
}
return foo;
}
]]>
</example>
</rule>
<rule name="UnsynchronizedStaticDateFormatter"
since="3.6"
deprecated="true"
message="Static DateFormatter objects should be accessed in a synchronized manner"
class="net.sourceforge.pmd.lang.java.rule.multithreading.UnsynchronizedStaticDateFormatterRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#unsynchronizedstaticdateformatter">
<description>
SimpleDateFormat instances are not synchronized. Sun recommends using separate format instances
for each thread. If multiple threads must access a static formatter, the formatter must be
synchronized either on method or block level.
This rule has been deprecated in favor of the rule {% rule UnsynchronizedStaticFormatter %}.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
private static final SimpleDateFormat sdf = new SimpleDateFormat();
void bar() {
sdf.format(); // poor, no thread-safety
}
synchronized void foo() {
sdf.format(); // preferred
}
}
]]>
</example>
</rule>
<rule name="UnsynchronizedStaticFormatter"
since="6.11.0"
message="Static Formatter objects should be accessed in a synchronized manner"
class="net.sourceforge.pmd.lang.java.rule.multithreading.UnsynchronizedStaticFormatterRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#unsynchronizedstaticformatter">
<description>
Instances of `java.text.Format` are generally not synchronized.
Sun recommends using separate format instances for each thread.
If multiple threads must access a static formatter, the formatter must be
synchronized either on method or block level.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
private static final SimpleDateFormat sdf = new SimpleDateFormat();
void bar() {
sdf.format(); // poor, no thread-safety
}
synchronized void foo() {
sdf.format(); // preferred
}
}
]]>
</example>
</rule>
<rule name="UseConcurrentHashMap"
language="java"
minimumLanguageVersion="1.5"
since="4.2.6"
message="If you run in Java5 or newer and have concurrent access, you should use the ConcurrentHashMap implementation"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#useconcurrenthashmap">
<description>
Since Java5 brought a new implementation of the Map designed for multi-threaded access, you can
perform efficient map reads without blocking other threads.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//Type[../VariableDeclarator/VariableInitializer//AllocationExpression/ClassOrInterfaceType[@Image != 'ConcurrentHashMap']]
/ReferenceType/ClassOrInterfaceType[@Image = 'Map']
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
public class ConcurrentApp {
public void getMyInstance() {
Map map1 = new HashMap(); // fine for single-threaded access
Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads
// the following case will be ignored by this rule
Map map3 = someModule.methodThatReturnMap(); // might be OK, if the returned map is already thread-safe
}
}
]]>
</example>
</rule>
<rule name="UseNotifyAllInsteadOfNotify"
language="java"
since="3.0"
message="Call Thread.notifyAll() rather than Thread.notify()"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#usenotifyallinsteadofnotify">
<description>
Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only
one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//StatementExpression/PrimaryExpression
[PrimarySuffix/Arguments[@ArgumentCount = '0']]
[
PrimaryPrefix[
./Name[@Image='notify' or ends-with(@Image,'.notify')]
or ../PrimarySuffix/@Image='notify'
or (./AllocationExpression and ../PrimarySuffix[@Image='notify'])
]
]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
void bar() {
x.notify();
// If many threads are monitoring x, only one (and you won't know which) will be notified.
// use instead:
x.notifyAll();
}
]]>
</example>
</rule>
</ruleset>

File diff suppressed because it is too large Load diff

View file

@ -1,65 +0,0 @@
<?xml version="1.0"?>
<ruleset name="Security" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Rules that flag potential security flaws.
</description>
<rule name="HardCodedCryptoKey"
since="6.4.0"
message="Do not use hard coded encryption keys"
class="net.sourceforge.pmd.lang.java.rule.security.HardCodedCryptoKeyRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_security.html#hardcodedcryptokey">
<description>
Do not use hard coded values for cryptographic operations. Please store keys outside of source code.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
void good() {
SecretKeySpec secretKeySpec = new SecretKeySpec(Properties.getKey(), "AES");
}
void bad() {
SecretKeySpec secretKeySpec = new SecretKeySpec("my secret here".getBytes(), "AES");
}
}
]]>
</example>
</rule>
<rule name="InsecureCryptoIv"
since="6.3.0"
message="Do not use hard coded initialization vector in crypto operations"
class="net.sourceforge.pmd.lang.java.rule.security.InsecureCryptoIvRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_security.html#insecurecryptoiv">
<description>
Do not use hard coded initialization vector in cryptographic operations. Please use a randomly generated IV.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
void good() {
SecureRandom random = new SecureRandom();
byte iv[] = new byte[16];
random.nextBytes(bytes);
}
void bad() {
byte[] iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, };
}
void alsoBad() {
byte[] iv = "secret iv in here".getBytes();
}
}
]]>
</example>
</rule>
</ruleset>

View file

@ -1,10 +0,0 @@
/*
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"
}
}
*/

View file

@ -1,13 +1,53 @@
/*
spotbugs { spotbugs {
effort = "max" effort = "max"
reportLevel = "low" reportLevel = "low"
ignoreFailures = true
} }
spotbugsMain { tasks.withType(com.github.spotbugs.SpotBugsTask) {
ignoreFailures = true
reports { reports {
xml.getRequired().set(false) xml.enabled = false
html.getRequired().set(true) 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"
}
}
*/

View file

@ -1,4 +0,0 @@
repositories {
mavenLocal()
mavenCentral()
}

View file

@ -1,7 +1,12 @@
def junitVersion = project.hasProperty('junit.version')?project.property('junit.version'):'5.6.2'
def hamcrestVersion = project.hasProperty('hamcrest.version')?project.property('hamcrest.version'):'2.2'
dependencies { dependencies {
testImplementation testLibs.junit.jupiter.api testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testImplementation testLibs.hamcrest testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
testRuntimeOnly testLibs.junit.jupiter.engine testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
} }
test { test {
@ -13,6 +18,9 @@ test {
'--add-opens=java.base/java.nio=ALL-UNNAMED' '--add-opens=java.base/java.nio=ALL-UNNAMED'
] ]
} }
systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
systemProperty 'path.home', "${project.buildDir}/"
systemProperty 'jna.debug_load', 'true'
failFast = true failFast = true
testLogging { testLogging {
events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED' events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED'

Binary file not shown.

View file

@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

282
gradlew vendored
View file

@ -1,7 +1,7 @@
#!/bin/sh #!/usr/bin/env sh
# #
# Copyright © 2015-2021 the original authors. # Copyright 2015 the original author or authors.
# #
# Licensed under the Apache License, Version 2.0 (the "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 not use this file except in compliance with the License.
@ -17,99 +17,67 @@
# #
############################################################################## ##############################################################################
# ##
# Gradle start up script for POSIX generated by Gradle. ## Gradle start up script for UN*X
# ##
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
app_path=$0 PRG="$0"
# Need this for relative symlinks.
# Need this for daisy-chained symlinks. while [ -h "$PRG" ] ; do
while ls=`ls -ld "$PRG"`
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path link=`expr "$ls" : '.*-> \(.*\)$'`
[ -h "$app_path" ] if expr "$link" : '/.*' > /dev/null; then
do PRG="$link"
ls=$( ls -ld "$app_path" ) else
link=${ls#*' -> '} PRG=`dirname "$PRG"`"/$link"
case $link in #( fi
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# This is normally unused APP_NAME="Gradle"
# shellcheck disable=SC2034 APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD="maximum"
warn () { warn () {
echo "$*" echo "$*"
} >&2 }
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} >&2 }
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "$( uname )" in #( case "`uname`" in
CYGWIN* ) cygwin=true ;; #( CYGWIN* )
Darwin* ) darwin=true ;; #( cygwin=true
MSYS* | MINGW* ) msys=true ;; #( ;;
NONSTOP* ) nonstop=true ;; Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -119,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java JAVACMD="$JAVA_HOME/jre/sh/java"
else else
JAVACMD=$JAVA_HOME/bin/java JAVACMD="$JAVA_HOME/bin/java"
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -130,120 +98,88 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD="java"
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
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 Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
case $MAX_FD in #( MAX_FD_LIMIT=`ulimit -H -n`
max*) if [ $? -eq 0 ] ; then
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
# shellcheck disable=SC2039,SC3045 MAX_FD="$MAX_FD_LIMIT"
MAX_FD=$( ulimit -H -n ) || fi
warn "Could not query maximum file descriptor limit" ulimit -n $MAX_FD
esac if [ $? -ne 0 ] ; then
case $MAX_FD in #( warn "Could not set maximum file descriptor limit: $MAX_FD"
'' | soft) :;; #( fi
*) else
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
# shellcheck disable=SC2039,SC3045 fi
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi fi
# Collect all arguments for the java command, stacking in reverse order: # For Darwin, add options to specify how the application appears in the dock
# * args from the command line if $darwin; then
# * the main class name GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
# * -classpath fi
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=`cygpath --unix "$JAVACMD"`
# Now convert the arguments - kludge to limit ourselves to /bin/sh # We build the pattern for arguments to be converted via cygpath
for arg do ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
if SEP=""
case $arg in #( for dir in $ROOTDIRSRAW ; do
-*) false ;; # don't mess with options #( ROOTDIRS="$ROOTDIRS$SEP$dir"
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath SEP="|"
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done 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=`expr $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 fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Collect all arguments for the java command, following the shell quoting and substitution rules
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

54
gradlew.bat vendored
View file

@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@ -25,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -41,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto init
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
@ -55,16 +54,31 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto init
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail 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 :execute
@rem Setup the command line @rem Setup the command line
@ -72,19 +86,17 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%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 :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View file

@ -1,14 +1 @@
dependencyResolutionManagement { rootProject.name = name
versionCatalogs {
libs {
version('gradle', '8.7')
}
testLibs {
version('junit', '5.10.2')
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit')
library('junit4', 'junit', 'junit').version('4.13.2')
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
}
}
}

View file

@ -79,15 +79,7 @@ public enum StandardSystemProperty {
/** Name of JIT compiler to use. */ /** Name of JIT compiler to use. */
JAVA_COMPILER("java.compiler"), JAVA_COMPILER("java.compiler"),
/** /** Path of extension directory or directories. */
* Path of extension directory or directories.
*
* @deprecated This property was <a
* href="https://openjdk.java.net/jeps/220#Removed:-The-extension-mechanism">deprecated</a> in
* Java 8 and removed in Java 9. We do not plan to remove this API from Guava, but if you are
* using it, it is probably not doing what you want.
*/
@Deprecated
JAVA_EXT_DIRS("java.ext.dirs"), JAVA_EXT_DIRS("java.ext.dirs"),
/** Operating system name. */ /** Operating system name. */

View file

@ -480,8 +480,8 @@ public final class CacheBuilder<K, V> {
this.maximumWeight); this.maximumWeight);
checkState( checkState(
this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize);
checkArgument(maximumWeight >= 0, "maximum weight must not be negative");
this.maximumWeight = maximumWeight; this.maximumWeight = maximumWeight;
checkArgument(maximumWeight >= 0, "maximum weight must not be negative");
return this; return this;
} }

View file

@ -35,6 +35,7 @@ import com.google.common.cache.CacheBuilder.NullListener;
import com.google.common.cache.CacheBuilder.OneWeigher; import com.google.common.cache.CacheBuilder.OneWeigher;
import com.google.common.cache.CacheLoader.InvalidCacheLoadException; import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
import com.google.common.cache.CacheLoader.UnsupportedLoadingOperationException; import com.google.common.cache.CacheLoader.UnsupportedLoadingOperationException;
import com.google.common.cache.LocalCache.AbstractCacheSet;
import com.google.common.collect.AbstractSequentialIterator; import com.google.common.collect.AbstractSequentialIterator;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -4283,7 +4284,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
public Set<K> keySet() { public Set<K> keySet() {
// does not impact recency ordering // does not impact recency ordering
Set<K> ks = keySet; Set<K> ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet()); return (ks != null) ? ks : (keySet = new KeySet(this));
} }
Collection<V> values; Collection<V> values;
@ -4292,7 +4293,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
public Collection<V> values() { public Collection<V> values() {
// does not impact recency ordering // does not impact recency ordering
Collection<V> vs = values; Collection<V> vs = values;
return (vs != null) ? vs : (values = new Values()); return (vs != null) ? vs : (values = new Values(this));
} }
Set<Entry<K, V>> entrySet; Set<Entry<K, V>> entrySet;
@ -4302,7 +4303,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
public Set<Entry<K, V>> entrySet() { public Set<Entry<K, V>> entrySet() {
// does not impact recency ordering // does not impact recency ordering
Set<Entry<K, V>> es = entrySet; Set<Entry<K, V>> es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet()); return (es != null) ? es : (entrySet = new EntrySet(this));
} }
// Iterator Support // Iterator Support
@ -4493,20 +4494,25 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
} }
abstract class AbstractCacheSet<T> extends AbstractSet<T> { abstract class AbstractCacheSet<T> extends AbstractSet<T> {
final ConcurrentMap<?, ?> map;
AbstractCacheSet(ConcurrentMap<?, ?> map) {
this.map = map;
}
@Override @Override
public int size() { public int size() {
return LocalCache.this.size(); return map.size();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return LocalCache.this.isEmpty(); return map.isEmpty();
} }
@Override @Override
public void clear() { public void clear() {
LocalCache.this.clear(); map.clear();
} }
// super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android.
@ -4550,6 +4556,10 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
final class KeySet extends AbstractCacheSet<K> { final class KeySet extends AbstractCacheSet<K> {
KeySet(ConcurrentMap<?, ?> map) {
super(map);
}
@Override @Override
public Iterator<K> iterator() { public Iterator<K> iterator() {
return new KeyIterator(); return new KeyIterator();
@ -4557,31 +4567,36 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
return LocalCache.this.containsKey(o); return map.containsKey(o);
} }
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
return LocalCache.this.remove(o) != null; return map.remove(o) != null;
} }
} }
final class Values extends AbstractCollection<V> { final class Values extends AbstractCollection<V> {
private final ConcurrentMap<?, ?> map;
Values(ConcurrentMap<?, ?> map) {
this.map = map;
}
@Override @Override
public int size() { public int size() {
return LocalCache.this.size(); return map.size();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return LocalCache.this.isEmpty(); return map.isEmpty();
} }
@Override @Override
public void clear() { public void clear() {
LocalCache.this.clear(); map.clear();
} }
@Override @Override
@ -4597,7 +4612,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
return LocalCache.this.containsValue(o); return map.containsValue(o);
} }
// super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android.
@ -4617,6 +4632,10 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
final class EntrySet extends AbstractCacheSet<Entry<K, V>> { final class EntrySet extends AbstractCacheSet<Entry<K, V>> {
EntrySet(ConcurrentMap<?, ?> map) {
super(map);
}
@Override @Override
public Iterator<Entry<K, V>> iterator() { public Iterator<Entry<K, V>> iterator() {
return new EntryIterator(); return new EntryIterator();

View file

@ -88,28 +88,6 @@ final class CartesianList<E> extends AbstractList<List<E>> implements RandomAcce
return computedIndex; return computedIndex;
} }
@Override
public int lastIndexOf(Object o) {
if (!(o instanceof List)) {
return -1;
}
List<?> list = (List<?>) o;
if (list.size() != axes.size()) {
return -1;
}
ListIterator<?> itr = list.listIterator();
int computedIndex = 0;
while (itr.hasNext()) {
int axisIndex = itr.nextIndex();
int elemIndex = axes.get(axisIndex).lastIndexOf(itr.next());
if (elemIndex == -1) {
return -1;
}
computedIndex += elemIndex * axesSizeProduct[axisIndex + 1];
}
return computedIndex;
}
@Override @Override
public ImmutableList<E> get(final int index) { public ImmutableList<E> get(final int index) {
checkElementIndex(index, size()); checkElementIndex(index, size());
@ -139,21 +117,8 @@ final class CartesianList<E> extends AbstractList<List<E>> implements RandomAcce
return axesSizeProduct[0]; return axesSizeProduct[0];
} }
public boolean contains(Object object) { @Override
if (!(object instanceof List)) { public boolean contains(Object o) {
return false; return indexOf(o) != -1;
}
List<?> list = (List<?>) object;
if (list.size() != axes.size()) {
return false;
}
int i = 0;
for (Object o : list) {
if (!axes.get(i).contains(o)) {
return false;
}
i++;
}
return true;
} }
} }

View file

@ -20,293 +20,60 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.TreeMap;
import java.util.function.BinaryOperator;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** Collectors utilities for {@code common.collect} internals. */ /** Collectors utilities for {@code common.collect} internals. */
@GwtCompatible @GwtCompatible
final class CollectCollectors { final class CollectCollectors {
static <T, K, V> Collector<T, ?, ImmutableBiMap<K, V>> toImmutableBiMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableBiMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableBiMap.Builder::combine,
ImmutableBiMap.Builder::build,
new Collector.Characteristics[0]);
}
private static final Collector<Object, ?, ImmutableList<Object>> TO_IMMUTABLE_LIST = private static final Collector<Object, ?, ImmutableList<Object>> TO_IMMUTABLE_LIST =
Collector.of( Collector.of(
ImmutableList::builder, ImmutableList::<Object>builder,
ImmutableList.Builder::add, ImmutableList.Builder::add,
ImmutableList.Builder::combine, ImmutableList.Builder::combine,
ImmutableList.Builder::build); ImmutableList.Builder::build);
private static final Collector<Object, ?, ImmutableSet<Object>> TO_IMMUTABLE_SET =
Collector.of(
ImmutableSet::builder,
ImmutableSet.Builder::add,
ImmutableSet.Builder::combine,
ImmutableSet.Builder::build);
@GwtIncompatible
private static final Collector<Range<Comparable<?>>, ?, ImmutableRangeSet<Comparable<?>>>
TO_IMMUTABLE_RANGE_SET =
Collector.of(
ImmutableRangeSet::builder,
ImmutableRangeSet.Builder::add,
ImmutableRangeSet.Builder::combine,
ImmutableRangeSet.Builder::build);
// Lists
@SuppressWarnings({"rawtypes", "unchecked"})
static <E> Collector<E, ?, ImmutableList<E>> toImmutableList() { static <E> Collector<E, ?, ImmutableList<E>> toImmutableList() {
return (Collector) TO_IMMUTABLE_LIST; return (Collector) TO_IMMUTABLE_LIST;
} }
// Sets static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableMap.Builder::combine,
ImmutableMap.Builder::build);
}
private static final Collector<Object, ?, ImmutableSet<Object>> TO_IMMUTABLE_SET =
Collector.of(
ImmutableSet::<Object>builder,
ImmutableSet.Builder::add,
ImmutableSet.Builder::combine,
ImmutableSet.Builder::build);
@SuppressWarnings({"rawtypes", "unchecked"})
static <E> Collector<E, ?, ImmutableSet<E>> toImmutableSet() { static <E> Collector<E, ?, ImmutableSet<E>> toImmutableSet() {
return (Collector) TO_IMMUTABLE_SET; return (Collector) TO_IMMUTABLE_SET;
} }
static <E> Collector<E, ?, ImmutableSortedSet<E>> toImmutableSortedSet(
Comparator<? super E> comparator) {
checkNotNull(comparator);
return Collector.of(
() -> new ImmutableSortedSet.Builder<E>(comparator),
ImmutableSortedSet.Builder::add,
ImmutableSortedSet.Builder::combine,
ImmutableSortedSet.Builder::build);
}
@SuppressWarnings({"rawtypes", "unchecked"})
static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() {
return (Collector) EnumSetAccumulator.TO_IMMUTABLE_ENUM_SET;
}
private static final class EnumSetAccumulator<E extends Enum<E>> {
@SuppressWarnings({"rawtypes", "unchecked"})
static final Collector<Enum<?>, ?, ImmutableSet<? extends Enum<?>>> TO_IMMUTABLE_ENUM_SET =
(Collector)
Collector.<Enum, EnumSetAccumulator, ImmutableSet<?>>of(
EnumSetAccumulator::new,
EnumSetAccumulator::add,
EnumSetAccumulator::combine,
EnumSetAccumulator::toImmutableSet,
Collector.Characteristics.UNORDERED);
private EnumSet<E> set;
void add(E e) {
if (set == null) {
set = EnumSet.of(e);
} else {
set.add(e);
}
}
EnumSetAccumulator<E> combine(EnumSetAccumulator<E> other) {
if (this.set == null) {
return other;
} else if (other.set == null) {
return this;
} else {
this.set.addAll(other.set);
return this;
}
}
ImmutableSet<E> toImmutableSet() {
return (set == null) ? ImmutableSet.<E>of() : ImmutableEnumSet.asImmutable(set);
}
}
@GwtIncompatible
@SuppressWarnings({"rawtypes", "unchecked"})
static <E extends Comparable<? super E>>
Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() {
return (Collector) TO_IMMUTABLE_RANGE_SET;
}
// Multisets
static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) {
checkNotNull(elementFunction);
checkNotNull(countFunction);
return Collector.of(
LinkedHashMultiset::create,
(multiset, t) ->
multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)),
(multiset1, multiset2) -> {
multiset1.addAll(multiset2);
return multiset1;
},
(Multiset<E> multiset) -> ImmutableMultiset.copyFromEntries(multiset.entrySet()));
}
static <T, E, M extends Multiset<E>> Collector<T, ?, M> toMultiset(
java.util.function.Function<? super T, E> elementFunction,
java.util.function.ToIntFunction<? super T> countFunction,
java.util.function.Supplier<M> multisetSupplier) {
checkNotNull(elementFunction);
checkNotNull(countFunction);
checkNotNull(multisetSupplier);
return Collector.of(
multisetSupplier,
(ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)),
(ms1, ms2) -> {
ms1.addAll(ms2);
return ms1;
});
}
// Maps
static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableMap.Builder::combine,
ImmutableMap.Builder::build);
}
public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new),
ImmutableMap::copyOf);
}
static <T, K, V> Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap(
Comparator<? super K> comparator,
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(comparator);
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(
keyFunction, valueFunction, mergeFunction, () -> new TreeMap<K, V>(comparator)),
ImmutableSortedMap::copyOfSorted);
}
static <T, K, V> Collector<T, ?, ImmutableBiMap<K, V>> toImmutableBiMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableBiMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableBiMap.Builder::combine,
ImmutableBiMap.Builder::build,
new Collector.Characteristics[0]);
}
static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
() ->
new EnumMapAccumulator<K, V>(
(v1, v2) -> {
throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2);
}),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
EnumMapAccumulator::combine,
EnumMapAccumulator::toImmutableMap,
Collector.Characteristics.UNORDERED);
}
static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
// not UNORDERED because we don't know if mergeFunction is commutative
return Collector.of(
() -> new EnumMapAccumulator<K, V>(mergeFunction),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
EnumMapAccumulator::combine,
EnumMapAccumulator::toImmutableMap);
}
private static class EnumMapAccumulator<K extends Enum<K>, V> {
private final BinaryOperator<V> mergeFunction;
private EnumMap<K, V> map = null;
EnumMapAccumulator(BinaryOperator<V> mergeFunction) {
this.mergeFunction = mergeFunction;
}
void put(K key, V value) {
if (map == null) {
map = new EnumMap<>(key.getDeclaringClass());
}
map.merge(key, value, mergeFunction);
}
EnumMapAccumulator<K, V> combine(EnumMapAccumulator<K, V> other) {
if (this.map == null) {
return other;
} else if (other.map == null) {
return this;
} else {
other.map.forEach(this::put);
return this;
}
}
ImmutableMap<K, V> toImmutableMap() {
return (map == null) ? ImmutableMap.<K, V>of() : ImmutableEnumMap.asImmutable(map);
}
}
@GwtIncompatible
static <T, K extends Comparable<? super K>, V>
Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap(
Function<? super T, Range<K>> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableRangeMap::<K, V>builder,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableRangeMap.Builder::combine,
ImmutableRangeMap.Builder::build);
}
static <T, K, V> Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap( static <T, K, V> Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap(
Comparator<? super K> comparator, Comparator<? super K> comparator,
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
@ -325,91 +92,43 @@ final class CollectCollectors {
ImmutableSortedMap.Builder::build, ImmutableSortedMap.Builder::build,
Collector.Characteristics.UNORDERED); Collector.Characteristics.UNORDERED);
} }
// Multimaps
static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap( static <E> Collector<E, ?, ImmutableSortedSet<E>> toImmutableSortedSet(
Function<? super T, ? extends K> keyFunction, Comparator<? super E> comparator) {
Function<? super T, ? extends V> valueFunction) { checkNotNull(comparator);
checkNotNull(keyFunction, "keyFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of( return Collector.of(
ImmutableListMultimap::<K, V>builder, () -> new ImmutableSortedSet.Builder<E>(comparator),
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), ImmutableSortedSet.Builder::add,
ImmutableListMultimap.Builder::combine, ImmutableSortedSet.Builder::combine,
ImmutableListMultimap.Builder::build); ImmutableSortedSet.Builder::build);
} }
static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap( @GwtIncompatible
Function<? super T, ? extends K> keyFunction, private static final Collector<Range<Comparable>, ?, ImmutableRangeSet<Comparable>>
Function<? super T, ? extends Stream<? extends V>> valuesFunction) { TO_IMMUTABLE_RANGE_SET =
checkNotNull(keyFunction); Collector.of(
checkNotNull(valuesFunction); ImmutableRangeSet::<Comparable>builder,
return Collectors.collectingAndThen( ImmutableRangeSet.Builder::add,
flatteningToMultimap( ImmutableRangeSet.Builder::combine,
input -> checkNotNull(keyFunction.apply(input)), ImmutableRangeSet.Builder::build);
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().arrayListValues()::<K, V>build), @GwtIncompatible
ImmutableListMultimap::copyOf); static <E extends Comparable<? super E>>
Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() {
return (Collector) TO_IMMUTABLE_RANGE_SET;
} }
static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap( @GwtIncompatible
Function<? super T, ? extends K> keyFunction, static <T, K extends Comparable<? super K>, V>
Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap(
Function<? super T, Range<K>> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction, "keyFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableSetMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableSetMultimap.Builder::combine,
ImmutableSetMultimap.Builder::build);
}
static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
checkNotNull(keyFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().linkedHashSetValues()::<K, V>build),
ImmutableSetMultimap::copyOf);
}
static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap(
java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<M> multimapSupplier) {
checkNotNull(keyFunction); checkNotNull(keyFunction);
checkNotNull(valueFunction); checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of( return Collector.of(
multimapSupplier, ImmutableRangeMap::<K, V>builder,
(multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)), (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
(multimap1, multimap2) -> { ImmutableRangeMap.Builder::combine,
multimap1.putAll(multimap2); ImmutableRangeMap.Builder::build);
return multimap1;
});
}
static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> flatteningToMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valueFunction,
Supplier<M> multimapSupplier) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of(
multimapSupplier,
(multimap, input) -> {
K key = keyFunction.apply(input);
Collection<V> valuesForKey = multimap.get(key);
valueFunction.apply(input).forEachOrdered(valuesForKey::add);
},
(multimap1, multimap2) -> {
multimap1.putAll(multimap2);
return multimap1;
});
} }
} }

View file

@ -352,6 +352,11 @@ public final class Collections2 {
return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO)); return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO));
} }
/** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */
static <T> Collection<T> cast(Iterable<T> iterable) {
return (Collection<T>) iterable;
}
/** /**
* Returns a {@link Collection} of all the permutations of the specified {@link Iterable}. * Returns a {@link Collection} of all the permutations of the specified {@link Iterable}.
* *

View file

@ -190,79 +190,4 @@ public final class Comparators {
checkNotNull(valueComparator); checkNotNull(valueComparator);
return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator)); return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator));
} }
/**
* Returns the minimum of the two values. If the values compare as 0, the first is returned.
*
* <p>The recommended solution for finding the {@code minimum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if less than or equal to b.
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i>.
* @since 30.0
*/
@Beta
public static <T extends Comparable<? super T>> T min(T a, T b) {
return (a.compareTo(b) <= 0) ? a : b;
}
/**
* Returns the minimum of the two values, according to the given comparator. If the values compare
* as equal, the first is returned.
*
* <p>The recommended solution for finding the {@code minimum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if less than or equal to b
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i> using the given
* comparator.
* @since 30.0
*/
@Beta
public static <T> T min(T a, T b, Comparator<T> comparator) {
return (comparator.compare(a, b) <= 0) ? a : b;
}
/**
* Returns the maximum of the two values. If the values compare as 0, the first is returned.
*
* <p>The recommended solution for finding the {@code maximum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if greater than or equal to b.
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i>.
* @since 30.0
*/
@Beta
public static <T extends Comparable<? super T>> T max(T a, T b) {
return (a.compareTo(b) >= 0) ? a : b;
}
/**
* Returns the maximum of the two values, according to the given comparator. If the values compare
* as equal, the first is returned.
*
* <p>The recommended solution for finding the {@code maximum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if greater than or equal to b.
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i> using the given
* comparator.
* @since 30.0
*/
@Beta
public static <T> T max(T a, T b, Comparator<T> comparator) {
return (comparator.compare(a, b) >= 0) ? a : b;
}
} }

View file

@ -191,14 +191,15 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte
/* /*
* These methods perform most headSet, subSet, and tailSet logic, besides parameter validation. * These methods perform most headSet, subSet, and tailSet logic, besides parameter validation.
*/ */
@SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT. // TODO(kevinb): we can probably make these real @Overrides now
/* @Override */
abstract ContiguousSet<C> headSetImpl(C toElement, boolean inclusive); abstract ContiguousSet<C> headSetImpl(C toElement, boolean inclusive);
@SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT. /* @Override */
abstract ContiguousSet<C> subSetImpl( abstract ContiguousSet<C> subSetImpl(
C fromElement, boolean fromInclusive, C toElement, boolean toInclusive); C fromElement, boolean fromInclusive, C toElement, boolean toInclusive);
@SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT. /* @Override */
abstract ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive); abstract ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive);
/** /**

View file

@ -786,7 +786,7 @@ public abstract class FluentIterable<E> implements Iterable<E> {
checkNotNull(collection); checkNotNull(collection);
Iterable<E> iterable = getDelegate(); Iterable<E> iterable = getDelegate();
if (iterable instanceof Collection) { if (iterable instanceof Collection) {
collection.addAll((Collection<E>) iterable); collection.addAll(Collections2.cast(iterable));
} else { } else {
for (E item : iterable) { for (E item : iterable) {
collection.add(item); collection.add(item);

View file

@ -76,8 +76,8 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject implements Ma
@Override @Override
public V remove(Object key) { public V remove(Object object) {
return delegate().remove(key); return delegate().remove(object);
} }
@Override @Override

View file

@ -413,14 +413,15 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableBiMapFauxverideShim<
* <p>Since the bimap is immutable, ImmutableBiMap doesn't require special logic for keeping the * <p>Since the bimap is immutable, ImmutableBiMap doesn't require special logic for keeping the
* bimap and its inverse in sync during serialization, the way AbstractBiMap does. * bimap and its inverse in sync during serialization, the way AbstractBiMap does.
*/ */
private static class SerializedForm<K, V> extends ImmutableMap.SerializedForm<K, V> { private static class SerializedForm extends ImmutableMap.SerializedForm {
SerializedForm(ImmutableBiMap<K, V> bimap) { SerializedForm(ImmutableBiMap<?, ?> bimap) {
super(bimap); super(bimap);
} }
@Override @Override
Builder<K, V> makeBuilder(int size) { Object readResolve() {
return new Builder<>(); Builder<Object, Object> builder = new Builder<>();
return createMap(builder);
} }
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
@ -428,6 +429,6 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableBiMapFauxverideShim<
@Override @Override
Object writeReplace() { Object writeReplace() {
return new SerializedForm<>(this); return new SerializedForm(this);
} }
} }

View file

@ -35,6 +35,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -82,7 +83,13 @@ public class ImmutableListMultimap<K, V> extends ImmutableMultimap<K, V>
public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap( public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
return CollectCollectors.toImmutableListMultimap(keyFunction, valueFunction); checkNotNull(keyFunction, "keyFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableListMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableListMultimap.Builder::combine,
ImmutableListMultimap.Builder::build);
} }
/** /**
@ -120,7 +127,14 @@ public class ImmutableListMultimap<K, V> extends ImmutableMultimap<K, V>
Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap( Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) { Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
return CollectCollectors.flatteningToImmutableListMultimap(keyFunction, valuesFunction); checkNotNull(keyFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
Multimaps.flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().arrayListValues()::<K, V>build),
ImmutableListMultimap::copyOf);
} }
/** Returns the empty multimap. */ /** Returns the empty multimap. */

View file

@ -36,6 +36,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.Spliterator; import java.util.Spliterator;
@ -95,7 +96,12 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction, Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
return CollectCollectors.toImmutableMap(keyFunction, valueFunction, mergeFunction); checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new),
ImmutableMap::copyOf);
} }
/** /**
@ -883,85 +889,37 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
* reconstructed using public factory methods. This ensures that the implementation types remain * reconstructed using public factory methods. This ensures that the implementation types remain
* as implementation details. * as implementation details.
*/ */
static class SerializedForm<K, V> implements Serializable { static class SerializedForm implements Serializable {
// This object retains references to collections returned by keySet() and value(). This saves private final Object[] keys;
// bytes when the both the map and its keySet or value collection are written to the same private final Object[] values;
// instance of ObjectOutputStream.
// TODO(b/160980469): remove support for the old serialization format after some time SerializedForm(ImmutableMap<?, ?> map) {
private static final boolean USE_LEGACY_SERIALIZATION = true; keys = new Object[map.size()];
values = new Object[map.size()];
private final Object keys; int i = 0;
private final Object values; for (Entry<?, ?> entry : map.entrySet()) {
keys[i] = entry.getKey();
SerializedForm(ImmutableMap<K, V> map) { values[i] = entry.getValue();
if (USE_LEGACY_SERIALIZATION) { i++;
Object[] keys = new Object[map.size()];
Object[] values = new Object[map.size()];
int i = 0;
for (Entry<?, ?> entry : map.entrySet()) {
keys[i] = entry.getKey();
values[i] = entry.getValue();
i++;
}
this.keys = keys;
this.values = values;
return;
} }
this.keys = map.keySet();
this.values = map.values();
} }
@SuppressWarnings("unchecked") Object readResolve() {
final Object readResolve() { Builder<Object, Object> builder = new Builder<>(keys.length);
if (!(this.keys instanceof ImmutableSet)) { return createMap(builder);
return legacyReadResolve();
}
ImmutableSet<K> keySet = (ImmutableSet<K>) this.keys;
ImmutableCollection<V> values = (ImmutableCollection<V>) this.values;
Builder<K, V> builder = makeBuilder(keySet.size());
UnmodifiableIterator<K> keyIter = keySet.iterator();
UnmodifiableIterator<V> valueIter = values.iterator();
while (keyIter.hasNext()) {
builder.put(keyIter.next(), valueIter.next());
}
return builder.build();
} }
@SuppressWarnings("unchecked") Object createMap(Builder<Object, Object> builder) {
final Object legacyReadResolve() {
K[] keys = (K[]) this.keys;
V[] values = (V[]) this.values;
Builder<K, V> builder = makeBuilder(keys.length);
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
builder.put(keys[i], values[i]); builder.put(keys[i], values[i]);
} }
return builder.build(); return builder.build();
} }
/**
* Returns a builder that builds the unserialized type. Subclasses should override this method.
*/
Builder<K, V> makeBuilder(int size) {
return new Builder<>(size);
}
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
} }
/**
* Returns a serializable form of this object. Non-public subclasses should not override this
* method. Publicly-accessible subclasses must override this method and should return a subclass
* of SerializedForm whose readResolve() method returns objects of the subclass type.
*/
Object writeReplace() { Object writeReplace() {
return new SerializedForm<>(this); return new SerializedForm(this);
} }
} }

View file

@ -75,6 +75,12 @@ final class ImmutableMapKeySet<K, V> extends IndexedImmutableSet<K> {
return true; return true;
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new KeySetSerializedForm<K>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class KeySetSerializedForm<K> implements Serializable { private static class KeySetSerializedForm<K> implements Serializable {
final ImmutableMap<K, ?> map; final ImmutableMap<K, ?> map;

View file

@ -100,6 +100,12 @@ final class ImmutableMapValues<K, V> extends ImmutableCollection<V> {
map.forEach((k, v) -> action.accept(v)); map.forEach((k, v) -> action.accept(v));
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new SerializedForm<V>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class SerializedForm<V> implements Serializable { private static class SerializedForm<V> implements Serializable {
final ImmutableMap<?, V> map; final ImmutableMap<?, V> map;

View file

@ -64,7 +64,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializa
* @since 21.0 * @since 21.0
*/ */
public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() { public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() {
return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1); return toImmutableMultiset(Function.identity(), e -> 1);
} }
/** /**
@ -80,7 +80,17 @@ public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializa
*/ */
public static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset( public static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) { Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) {
return CollectCollectors.toImmutableMultiset(elementFunction, countFunction); checkNotNull(elementFunction);
checkNotNull(countFunction);
return Collector.of(
LinkedHashMultiset::create,
(multiset, t) ->
multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)),
(multiset1, multiset2) -> {
multiset1.addAll(multiset2);
return multiset1;
},
(Multiset<E> multiset) -> copyFromEntries(multiset.entrySet()));
} }
/** Returns the empty immutable multiset. */ /** Returns the empty immutable multiset. */

View file

@ -514,11 +514,13 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements
} }
@Override @Override
public Builder<E> add(E... elements) { public Builder<E> add(E... elements) {
super.add(elements); super.add(elements);
return this; return this;
} }
@Override
/** /**
* Adds each element of {@code elements} to the {@code ImmutableSet}, ignoring duplicate * Adds each element of {@code elements} to the {@code ImmutableSet}, ignoring duplicate
* elements (only the first duplicate element is added). * elements (only the first duplicate element is added).
@ -527,7 +529,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements
* @return this {@code Builder} object * @return this {@code Builder} object
* @throws NullPointerException if {@code elements} is null or contains a null element * @throws NullPointerException if {@code elements} is null or contains a null element
*/ */
@Override
public Builder<E> addAll(Iterable<? extends E> elements) { public Builder<E> addAll(Iterable<? extends E> elements) {
super.addAll(elements); super.addAll(elements);
return this; return this;

View file

@ -22,6 +22,7 @@ import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
@ -37,6 +38,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -85,7 +87,13 @@ public class ImmutableSetMultimap<K, V> extends ImmutableMultimap<K, V>
public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap( public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
return CollectCollectors.toImmutableSetMultimap(keyFunction, valueFunction); checkNotNull(keyFunction, "keyFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableSetMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableSetMultimap.Builder::combine,
ImmutableSetMultimap.Builder::build);
} }
/** /**
@ -132,7 +140,14 @@ public class ImmutableSetMultimap<K, V> extends ImmutableMultimap<K, V>
Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap( Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) { Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
return CollectCollectors.flatteningToImmutableSetMultimap(keyFunction, valuesFunction); checkNotNull(keyFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
Multimaps.flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().linkedHashSetValues()::<K, V>build),
ImmutableSetMultimap::copyOf);
} }
/** Returns the empty multimap. */ /** Returns the empty multimap. */

View file

@ -95,7 +95,14 @@ public final class ImmutableSortedMap<K, V> extends ImmutableSortedMapFauxveride
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction, Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
return CollectCollectors.toImmutableSortedMap(comparator, keyFunction, valueFunction, mergeFunction); checkNotNull(comparator);
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(
keyFunction, valueFunction, mergeFunction, () -> new TreeMap<K, V>(comparator)),
ImmutableSortedMap::copyOfSorted);
} }
/* /*
@ -909,18 +916,19 @@ public final class ImmutableSortedMap<K, V> extends ImmutableSortedMapFauxveride
* are reconstructed using public factory methods. This ensures that the implementation types * are reconstructed using public factory methods. This ensures that the implementation types
* remain as implementation details. * remain as implementation details.
*/ */
private static class SerializedForm<K, V> extends ImmutableMap.SerializedForm<K, V> { private static class SerializedForm extends ImmutableMap.SerializedForm {
private final Comparator<? super K> comparator; private final Comparator<Object> comparator;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
SerializedForm(ImmutableSortedMap<K, V> sortedMap) { SerializedForm(ImmutableSortedMap<?, ?> sortedMap) {
super(sortedMap); super(sortedMap);
comparator = sortedMap.comparator(); comparator = (Comparator<Object>) sortedMap.comparator();
} }
@Override @Override
Builder<K, V> makeBuilder(int size) { Object readResolve() {
return new Builder<>(comparator); Builder<Object, Object> builder = new Builder<>(comparator);
return createMap(builder);
} }
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
@ -928,7 +936,7 @@ public final class ImmutableSortedMap<K, V> extends ImmutableSortedMapFauxveride
@Override @Override
Object writeReplace() { Object writeReplace() {
return new SerializedForm<>(this); return new SerializedForm(this);
} }
// This class is never actually serialized directly, but we have to make the // This class is never actually serialized directly, but we have to make the

View file

@ -20,8 +20,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.collect.Tables.AbstractCell;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -61,7 +63,15 @@ public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
Function<? super T, ? extends R> rowFunction, Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction, Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction); checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
() -> new ImmutableTable.Builder<R, C, V>(),
(builder, t) ->
builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)),
(b1, b2) -> b1.combine(b2),
b -> b.build());
} }
/** /**
@ -80,7 +90,88 @@ public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
Function<? super T, ? extends C> columnFunction, Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction, Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction, mergeFunction);
checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
checkNotNull(mergeFunction, "mergeFunction");
/*
* No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but
* the Builder can't efficiently support merging of duplicate values. Getting around this
* requires some work.
*/
return Collector.of(
() -> new CollectorState<R, C, V>()
/* GWT isn't currently playing nicely with constructor references? */ ,
(state, input) ->
state.put(
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(s1, s2) -> s1.combine(s2, mergeFunction),
state -> state.toTable());
}
private static final class CollectorState<R, C, V> {
final List<MutableCell<R, C, V>> insertionOrder = new ArrayList<>();
final Table<R, C, MutableCell<R, C, V>> table = HashBasedTable.create();
void put(R row, C column, V value, BinaryOperator<V> merger) {
MutableCell<R, C, V> oldCell = table.get(row, column);
if (oldCell == null) {
MutableCell<R, C, V> cell = new MutableCell<>(row, column, value);
insertionOrder.add(cell);
table.put(row, column, cell);
} else {
oldCell.merge(value, merger);
}
}
CollectorState<R, C, V> combine(CollectorState<R, C, V> other, BinaryOperator<V> merger) {
for (MutableCell<R, C, V> cell : other.insertionOrder) {
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger);
}
return this;
}
ImmutableTable<R, C, V> toTable() {
return copyOf(insertionOrder);
}
}
private static final class MutableCell<R, C, V> extends AbstractCell<R, C, V> {
private final R row;
private final C column;
private V value;
MutableCell(R row, C column, V value) {
this.row = checkNotNull(row, "row");
this.column = checkNotNull(column, "column");
this.value = checkNotNull(value, "value");
}
@Override
public R getRowKey() {
return row;
}
@Override
public C getColumnKey() {
return column;
}
@Override
public V getValue() {
return value;
}
void merge(V value, BinaryOperator<V> mergeFunction) {
checkNotNull(value, "value");
this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply");
}
} }
/** Returns an empty immutable table. */ /** Returns an empty immutable table. */
@ -118,7 +209,7 @@ public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
} }
} }
public static <R, C, V> ImmutableTable<R, C, V> copyOf( private static <R, C, V> ImmutableTable<R, C, V> copyOf(
Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) { Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) {
ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder(); ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder();
for (Cell<? extends R, ? extends C, ? extends V> cell : cells) { for (Cell<? extends R, ? extends C, ? extends V> cell : cells) {

View file

@ -21,12 +21,8 @@ import com.google.common.annotations.GwtIncompatible;
/** /**
* Provides similar behavior to {@link String#intern} for any immutable type. Common implementations * Provides equivalent behavior to {@link String#intern} for other immutable types. Common
* are available from the {@link Interners} class. * implementations are available from the {@link Interners} class.
*
* <p>Note that {@code String.intern()} has some well-known performance limitations, and should
* generally be avoided. Prefer {@link Interners#newWeakInterner} or another {@code Interner}
* implementation even for {@code String} interning.
* *
* @author Kevin Bourrillion * @author Kevin Bourrillion
* @since 3.0 * @since 3.0

View file

@ -314,7 +314,7 @@ public final class Iterables {
public static <T> boolean addAll(Collection<T> addTo, Iterable<? extends T> elementsToAdd) { public static <T> boolean addAll(Collection<T> addTo, Iterable<? extends T> elementsToAdd) {
if (elementsToAdd instanceof Collection) { if (elementsToAdd instanceof Collection) {
Collection<? extends T> c = (Collection<? extends T>) elementsToAdd; Collection<? extends T> c = Collections2.cast(elementsToAdd);
return addTo.addAll(c); return addTo.addAll(c);
} }
return Iterators.addAll(addTo, checkNotNull(elementsToAdd).iterator()); return Iterators.addAll(addTo, checkNotNull(elementsToAdd).iterator());
@ -814,7 +814,7 @@ public final class Iterables {
*/ */
public static <T> T getLast(Iterable<? extends T> iterable, T defaultValue) { public static <T> T getLast(Iterable<? extends T> iterable, T defaultValue) {
if (iterable instanceof Collection) { if (iterable instanceof Collection) {
Collection<? extends T> c = (Collection<? extends T>) iterable; Collection<? extends T> c = Collections2.cast(iterable);
if (c.isEmpty()) { if (c.isEmpty()) {
return defaultValue; return defaultValue;
} else if (iterable instanceof List) { } else if (iterable instanceof List) {

View file

@ -126,8 +126,8 @@ public final class Lists {
checkNotNull(elements); // for GWT checkNotNull(elements); // for GWT
// Let ArrayList's sizing logic work, if possible // Let ArrayList's sizing logic work, if possible
return (elements instanceof Collection) return (elements instanceof Collection)
? new ArrayList<>((Collection<? extends E>) elements) ? new ArrayList<>(Collections2.cast(elements))
: newArrayList(elements.iterator()); : newArrayList(elements.iterator());
} }
/** /**
@ -264,9 +264,8 @@ public final class Lists {
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
// We copy elements to an ArrayList first, rather than incurring the // We copy elements to an ArrayList first, rather than incurring the
// quadratic cost of adding them to the COWAL directly. // quadratic cost of adding them to the COWAL directly.
Collection<? extends E> elementsCollection = (elements instanceof Collection) Collection<? extends E> elementsCollection =
? (Collection<? extends E>) elements (elements instanceof Collection) ? Collections2.cast(elements) : newArrayList(elements);
: newArrayList(elements);
return new CopyOnWriteArrayList<>(elementsCollection); return new CopyOnWriteArrayList<>(elementsCollection);
} }

View file

@ -64,7 +64,7 @@ import java.util.concurrent.locks.ReentrantLock;
* @author Charles Fry * @author Charles Fry
* @author Doug Lea ({@code ConcurrentHashMap}) * @author Doug Lea ({@code ConcurrentHashMap})
*/ */
// TODO(kak): Consider removing @CanIgnoreReturnValue from this class. // TODO(kak/cpovirk): Consider removing from this class.
@GwtIncompatible @GwtIncompatible
@SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress. @SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress.
class MapMakerInternalMap< class MapMakerInternalMap<

View file

@ -172,6 +172,37 @@ public final class Maps {
return ImmutableEnumMap.asImmutable(enumMap); return ImmutableEnumMap.asImmutable(enumMap);
} }
private static class Accumulator<K extends Enum<K>, V> {
private final BinaryOperator<V> mergeFunction;
private EnumMap<K, V> map = null;
Accumulator(BinaryOperator<V> mergeFunction) {
this.mergeFunction = mergeFunction;
}
void put(K key, V value) {
if (map == null) {
map = new EnumMap<>(key.getDeclaringClass());
}
map.merge(key, value, mergeFunction);
}
Accumulator<K, V> combine(Accumulator<K, V> other) {
if (this.map == null) {
return other;
} else if (other.map == null) {
return this;
} else {
other.map.forEach(this::put);
return this;
}
}
ImmutableMap<K, V> toImmutableMap() {
return (map == null) ? ImmutableMap.<K, V>of() : ImmutableEnumMap.asImmutable(map);
}
}
/** /**
* Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys
* and values are the result of applying the provided mapping functions to the input elements. The * and values are the result of applying the provided mapping functions to the input elements. The
@ -189,7 +220,22 @@ public final class Maps {
public static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap( public static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap(
java.util.function.Function<? super T, ? extends K> keyFunction, java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction) { java.util.function.Function<? super T, ? extends V> valueFunction) {
return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction); checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
() ->
new Accumulator<K, V>(
(v1, v2) -> {
throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2);
}),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
Accumulator::combine,
Accumulator::toImmutableMap,
Collector.Characteristics.UNORDERED);
} }
/** /**
@ -207,7 +253,19 @@ public final class Maps {
java.util.function.Function<? super T, ? extends K> keyFunction, java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction, mergeFunction); checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
// not UNORDERED because we don't know if mergeFunction is commutative
return Collector.of(
() -> new Accumulator<K, V>(mergeFunction),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
Accumulator::combine,
Accumulator::toImmutableMap);
} }
/** /**

View file

@ -49,7 +49,6 @@ import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -105,11 +104,21 @@ public final class Multimaps {
* *
* @since 21.0 * @since 21.0
*/ */
@Beta
public static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap( public static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap(
java.util.function.Function<? super T, ? extends K> keyFunction, java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<M> multimapSupplier) { java.util.function.Supplier<M> multimapSupplier) {
return CollectCollectors.toMultimap(keyFunction, valueFunction, multimapSupplier); checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of(
multimapSupplier,
(multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)),
(multimap1, multimap2) -> {
multimap1.putAll(multimap2);
return multimap1;
});
} }
/** /**
@ -686,11 +695,6 @@ public final class Multimaps {
return result; return result;
} }
@Override
public void forEach(BiConsumer<? super K, ? super V> consumer) {
delegate.forEach(checkNotNull(consumer));
}
@Override @Override
public Collection<V> get(K key) { public Collection<V> get(K key) {
return unmodifiableValueCollection(delegate.get(key)); return unmodifiableValueCollection(delegate.get(key));

View file

@ -77,7 +77,16 @@ public final class Multisets {
java.util.function.Function<? super T, E> elementFunction, java.util.function.Function<? super T, E> elementFunction,
java.util.function.ToIntFunction<? super T> countFunction, java.util.function.ToIntFunction<? super T> countFunction,
java.util.function.Supplier<M> multisetSupplier) { java.util.function.Supplier<M> multisetSupplier) {
return CollectCollectors.toMultiset(elementFunction, countFunction, multisetSupplier); checkNotNull(elementFunction);
checkNotNull(countFunction);
checkNotNull(multisetSupplier);
return Collector.of(
multisetSupplier,
(ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)),
(ms1, ms2) -> {
ms1.addAll(ms2);
return ms1;
});
} }
/** /**

View file

@ -582,8 +582,8 @@ public abstract class Ordering<T> implements Comparator<T> {
* <p><b>Implementation note:</b> this method is invoked by the default implementations of the * <p><b>Implementation note:</b> this method is invoked by the default implementations of the
* other {@code min} overloads, so overriding it will affect their behavior. * other {@code min} overloads, so overriding it will affect their behavior.
* *
* <p><b>Note:</b> Consider using {@code Comparators.min(a, b, thisComparator)} instead. If {@code * <p><b>Java 8 users:</b> Use {@code Collections.min(Arrays.asList(a, b), thisComparator)}
* thisComparator} is {@link Ordering#natural}, then use {@code Comparators.min(a, b)}. * instead (but note that it does not guarantee which tied minimum element is returned).
* *
* @param a value to compare, returned if less than or equal to b. * @param a value to compare, returned if less than or equal to b.
* @param b value to compare. * @param b value to compare.

View file

@ -74,7 +74,7 @@ public final class Queues {
*/ */
public static <E> ArrayDeque<E> newArrayDeque(Iterable<? extends E> elements) { public static <E> ArrayDeque<E> newArrayDeque(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new ArrayDeque<E>((Collection<? extends E>) elements); return new ArrayDeque<E>(Collections2.cast(elements));
} }
ArrayDeque<E> deque = new ArrayDeque<E>(); ArrayDeque<E> deque = new ArrayDeque<E>();
Iterables.addAll(deque, elements); Iterables.addAll(deque, elements);
@ -97,7 +97,7 @@ public final class Queues {
public static <E> ConcurrentLinkedQueue<E> newConcurrentLinkedQueue( public static <E> ConcurrentLinkedQueue<E> newConcurrentLinkedQueue(
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new ConcurrentLinkedQueue<E>((Collection<? extends E>) elements); return new ConcurrentLinkedQueue<E>(Collections2.cast(elements));
} }
ConcurrentLinkedQueue<E> queue = new ConcurrentLinkedQueue<E>(); ConcurrentLinkedQueue<E> queue = new ConcurrentLinkedQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -137,7 +137,7 @@ public final class Queues {
@GwtIncompatible // LinkedBlockingDeque @GwtIncompatible // LinkedBlockingDeque
public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(Iterable<? extends E> elements) { public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new LinkedBlockingDeque<E>((Collection<? extends E>) elements); return new LinkedBlockingDeque<E>(Collections2.cast(elements));
} }
LinkedBlockingDeque<E> deque = new LinkedBlockingDeque<E>(); LinkedBlockingDeque<E> deque = new LinkedBlockingDeque<E>();
Iterables.addAll(deque, elements); Iterables.addAll(deque, elements);
@ -173,7 +173,7 @@ public final class Queues {
@GwtIncompatible // LinkedBlockingQueue @GwtIncompatible // LinkedBlockingQueue
public static <E> LinkedBlockingQueue<E> newLinkedBlockingQueue(Iterable<? extends E> elements) { public static <E> LinkedBlockingQueue<E> newLinkedBlockingQueue(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new LinkedBlockingQueue<E>((Collection<? extends E>) elements); return new LinkedBlockingQueue<E>(Collections2.cast(elements));
} }
LinkedBlockingQueue<E> queue = new LinkedBlockingQueue<E>(); LinkedBlockingQueue<E> queue = new LinkedBlockingQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -207,7 +207,7 @@ public final class Queues {
public static <E extends Comparable> PriorityBlockingQueue<E> newPriorityBlockingQueue( public static <E extends Comparable> PriorityBlockingQueue<E> newPriorityBlockingQueue(
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new PriorityBlockingQueue<E>((Collection<? extends E>) elements); return new PriorityBlockingQueue<E>(Collections2.cast(elements));
} }
PriorityBlockingQueue<E> queue = new PriorityBlockingQueue<E>(); PriorityBlockingQueue<E> queue = new PriorityBlockingQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -237,7 +237,7 @@ public final class Queues {
public static <E extends Comparable> PriorityQueue<E> newPriorityQueue( public static <E extends Comparable> PriorityQueue<E> newPriorityQueue(
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new PriorityQueue<E>((Collection<? extends E>) elements); return new PriorityQueue<E>(Collections2.cast(elements));
} }
PriorityQueue<E> queue = new PriorityQueue<E>(); PriorityQueue<E> queue = new PriorityQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -322,7 +322,7 @@ public final class Queues {
} }
/** /**
* Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, but with a * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, Duration)}, but with a
* different behavior in case it is interrupted while waiting. In that case, the operation will * different behavior in case it is interrupted while waiting. In that case, the operation will
* continue as usual, and in the end the thread's interruption status will be set (no {@code * continue as usual, and in the end the thread's interruption status will be set (no {@code
* InterruptedException} is thrown). * InterruptedException} is thrown).

View file

@ -576,21 +576,6 @@ public final class Range<C extends Comparable> extends RangeGwtSerializationDepe
* @since 27.0 * @since 27.0
*/ */
public Range<C> gap(Range<C> otherRange) { public Range<C> gap(Range<C> otherRange) {
/*
* For an explanation of the basic principle behind this check, see
* https://stackoverflow.com/a/35754308/28465
*
* In that explanation's notation, our `overlap` check would be `x1 < y2 && y1 < x2`. We've
* flipped one part of the check so that we're using "less than" in both cases (rather than a
* mix of "less than" and "greater than"). We've also switched to "strictly less than" rather
* than "less than or equal to" because of *handwave* the difference between "endpoints of
* inclusive ranges" and "Cuts."
*/
if (lowerBound.compareTo(otherRange.upperBound) < 0
&& otherRange.lowerBound.compareTo(upperBound) < 0) {
throw new IllegalArgumentException(
"Ranges have a nonempty intersection: " + this + ", " + otherRange);
}
boolean isThisFirst = this.lowerBound.compareTo(otherRange.lowerBound) < 0; boolean isThisFirst = this.lowerBound.compareTo(otherRange.lowerBound) < 0;
Range<C> firstRange = isThisFirst ? this : otherRange; Range<C> firstRange = isThisFirst ? this : otherRange;
Range<C> secondRange = isThisFirst ? otherRange : this; Range<C> secondRange = isThisFirst ? otherRange : this;

View file

@ -207,10 +207,10 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
} }
@GwtCompatible(emulated = true) @GwtCompatible(emulated = true)
private static final class KeySet<K> extends IndexedImmutableSet<K> { private static final class KeySet<K, V> extends IndexedImmutableSet<K> {
private final RegularImmutableMap<K, ?> map; private final RegularImmutableMap<K, V> map;
KeySet(RegularImmutableMap<K, ?> map) { KeySet(RegularImmutableMap<K, V> map) {
this.map = map; this.map = map;
} }
@ -234,6 +234,12 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
return map.size(); return map.size();
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new SerializedForm<K>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class SerializedForm<K> implements Serializable { private static class SerializedForm<K> implements Serializable {
final ImmutableMap<K, ?> map; final ImmutableMap<K, ?> map;
@ -278,6 +284,12 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
return true; return true;
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new SerializedForm<V>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class SerializedForm<V> implements Serializable { private static class SerializedForm<V> implements Serializable {
final ImmutableMap<?, V> map; final ImmutableMap<?, V> map;

View file

@ -138,6 +138,42 @@ public final class Sets {
} }
} }
private static final class Accumulator<E extends Enum<E>> {
static final Collector<Enum<?>, ?, ImmutableSet<? extends Enum<?>>> TO_IMMUTABLE_ENUM_SET =
(Collector)
Collector.<Enum, Accumulator, ImmutableSet<?>>of(
Accumulator::new,
Accumulator::add,
Accumulator::combine,
Accumulator::toImmutableSet,
Collector.Characteristics.UNORDERED);
private EnumSet<E> set;
void add(E e) {
if (set == null) {
set = EnumSet.of(e);
} else {
set.add(e);
}
}
Accumulator<E> combine(Accumulator<E> other) {
if (this.set == null) {
return other;
} else if (other.set == null) {
return this;
} else {
this.set.addAll(other.set);
return this;
}
}
ImmutableSet<E> toImmutableSet() {
return (set == null) ? ImmutableSet.<E>of() : ImmutableEnumSet.asImmutable(set);
}
}
/** /**
* Returns a {@code Collector} that accumulates the input elements into a new {@code ImmutableSet} * Returns a {@code Collector} that accumulates the input elements into a new {@code ImmutableSet}
* with an implementation specialized for enums. Unlike {@link ImmutableSet#toImmutableSet}, the * with an implementation specialized for enums. Unlike {@link ImmutableSet#toImmutableSet}, the
@ -146,7 +182,7 @@ public final class Sets {
* @since 21.0 * @since 21.0
*/ */
public static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() { public static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() {
return CollectCollectors.toImmutableEnumSet(); return (Collector) Accumulator.TO_IMMUTABLE_ENUM_SET;
} }
/** /**
@ -218,8 +254,8 @@ public final class Sets {
*/ */
public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements) { public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements) {
return (elements instanceof Collection) return (elements instanceof Collection)
? new HashSet<E>((Collection<? extends E>) elements) ? new HashSet<E>(Collections2.cast(elements))
: newHashSet(elements.iterator()); : newHashSet(elements.iterator());
} }
/** /**
@ -323,7 +359,7 @@ public final class Sets {
*/ */
public static <E> LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements) { public static <E> LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new LinkedHashSet<E>((Collection<? extends E>) elements); return new LinkedHashSet<E>(Collections2.cast(elements));
} }
LinkedHashSet<E> set = newLinkedHashSet(); LinkedHashSet<E> set = newLinkedHashSet();
Iterables.addAll(set, elements); Iterables.addAll(set, elements);
@ -451,8 +487,8 @@ public final class Sets {
// quadratic cost of adding them to the COWAS directly. // quadratic cost of adding them to the COWAS directly.
Collection<? extends E> elementsCollection = Collection<? extends E> elementsCollection =
(elements instanceof Collection) (elements instanceof Collection)
? (Collection<? extends E>) elements ? Collections2.cast(elements)
: Lists.newArrayList(elements); : Lists.newArrayList(elements);
return new CopyOnWriteArraySet<E>(elementsCollection); return new CopyOnWriteArraySet<E>(elementsCollection);
} }
@ -1391,25 +1427,6 @@ public final class Sets {
return delegate; return delegate;
} }
@Override
public boolean contains(Object object) {
if (!(object instanceof List)) {
return false;
}
List<?> list = (List<?>) object;
if (list.size() != axes.size()) {
return false;
}
int i = 0;
for (Object o : list) {
if (!axes.get(i).contains(o)) {
return false;
}
i++;
}
return true;
}
@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
// Warning: this is broken if size() == 0, so it is critical that we // Warning: this is broken if size() == 0, so it is critical that we
@ -1559,7 +1576,7 @@ public final class Sets {
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof PowerSet) { if (obj instanceof PowerSet) {
PowerSet<?> that = (PowerSet<?>) obj; PowerSet<?> that = (PowerSet<?>) obj;
return inputSet.keySet().equals(that.inputSet.keySet()); return inputSet.equals(that.inputSet);
} }
return super.equals(obj); return super.equals(obj);
} }

View file

@ -42,7 +42,6 @@ final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> {
checkEntryNotNull(singleKey, singleValue); checkEntryNotNull(singleKey, singleValue);
this.singleKey = singleKey; this.singleKey = singleKey;
this.singleValue = singleValue; this.singleValue = singleValue;
this.inverse = null;
} }
private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap<V, K> inverse) { private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap<V, K> inverse) {
@ -93,21 +92,14 @@ final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> {
transient ImmutableBiMap<V, K> inverse; transient ImmutableBiMap<V, K> inverse;
private transient ImmutableBiMap<V, K> lazyInverse;
@Override @Override
public ImmutableBiMap<V, K> inverse() { public ImmutableBiMap<V, K> inverse() {
// racy single-check idiom // racy single-check idiom
if (inverse != null) { ImmutableBiMap<V, K> result = inverse;
return inverse; if (result == null) {
return inverse = new SingletonImmutableBiMap<>(singleValue, singleKey, this);
} else { } else {
// racy single-check idiom return result;
ImmutableBiMap<V, K> result = lazyInverse;
if (result == null) {
return lazyInverse = new SingletonImmutableBiMap<>(singleValue, singleKey, this);
} else {
return result;
}
} }
} }
} }

View file

@ -1,200 +0,0 @@
/*
* 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.checkNotNull;
import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.Tables.AbstractCell;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
/** Collectors utilities for {@code common.collect.Table} internals. */
@GwtCompatible
final class TableCollectors {
static <T, R, C, V> Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
(Supplier<ImmutableTable.Builder<R, C, V>>) ImmutableTable.Builder::new,
(builder, t) ->
builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)),
ImmutableTable.Builder::combine,
ImmutableTable.Builder::build);
}
static <T, R, C, V> Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
checkNotNull(mergeFunction, "mergeFunction");
/*
* No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but
* the Builder can't efficiently support merging of duplicate values. Getting around this
* requires some work.
*/
return Collector.of(
() -> new ImmutableTableCollectorState<R, C, V>()
/* GWT isn't currently playing nicely with constructor references? */ ,
(state, input) ->
state.put(
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(s1, s2) -> s1.combine(s2, mergeFunction),
state -> state.toTable());
}
static <T, R, C, V, I extends Table<R, C, V>> Collector<T, ?, I> toTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction,
Supplier<I> tableSupplier) {
return toTable(
rowFunction,
columnFunction,
valueFunction,
(v1, v2) -> {
throw new IllegalStateException("Conflicting values " + v1 + " and " + v2);
},
tableSupplier);
}
static <T, R, C, V, I extends Table<R, C, V>> Collector<T, ?, I> toTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction,
Supplier<I> tableSupplier) {
checkNotNull(rowFunction);
checkNotNull(columnFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
checkNotNull(tableSupplier);
return Collector.of(
tableSupplier,
(table, input) ->
mergeTables(
table,
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(table1, table2) -> {
for (Table.Cell<R, C, V> cell2 : table2.cellSet()) {
mergeTables(
table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction);
}
return table1;
});
}
private static final class ImmutableTableCollectorState<R, C, V> {
final List<MutableCell<R, C, V>> insertionOrder = new ArrayList<>();
final Table<R, C, MutableCell<R, C, V>> table = HashBasedTable.create();
void put(R row, C column, V value, BinaryOperator<V> merger) {
MutableCell<R, C, V> oldCell = table.get(row, column);
if (oldCell == null) {
MutableCell<R, C, V> cell = new MutableCell<>(row, column, value);
insertionOrder.add(cell);
table.put(row, column, cell);
} else {
oldCell.merge(value, merger);
}
}
ImmutableTableCollectorState<R, C, V> combine(
ImmutableTableCollectorState<R, C, V> other, BinaryOperator<V> merger) {
for (MutableCell<R, C, V> cell : other.insertionOrder) {
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger);
}
return this;
}
ImmutableTable<R, C, V> toTable() {
return ImmutableTable.copyOf(insertionOrder);
}
}
private static final class MutableCell<R, C, V> extends AbstractCell<R, C, V> {
private final R row;
private final C column;
private V value;
MutableCell(R row, C column, V value) {
this.row = checkNotNull(row, "row");
this.column = checkNotNull(column, "column");
this.value = checkNotNull(value, "value");
}
@Override
public R getRowKey() {
return row;
}
@Override
public C getColumnKey() {
return column;
}
@Override
public V getValue() {
return value;
}
void merge(V value, BinaryOperator<V> mergeFunction) {
checkNotNull(value, "value");
this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply");
}
}
private static <R, C, V> void mergeTables(
Table<R, C, V> table, R row, C column, V value, BinaryOperator<V> mergeFunction) {
checkNotNull(value);
V oldValue = table.get(row, column);
if (oldValue == null) {
table.put(row, column, value);
} else {
V newValue = mergeFunction.apply(oldValue, value);
if (newValue == null) {
table.remove(row, column);
} else {
table.put(row, column, newValue);
}
}
}
private TableCollectors() {}
}

View file

@ -68,7 +68,14 @@ public final class Tables {
java.util.function.Function<? super T, ? extends C> columnFunction, java.util.function.Function<? super T, ? extends C> columnFunction,
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<I> tableSupplier) { java.util.function.Supplier<I> tableSupplier) {
return TableCollectors.toTable(rowFunction, columnFunction, valueFunction, tableSupplier); return toTable(
rowFunction,
columnFunction,
valueFunction,
(v1, v2) -> {
throw new IllegalStateException("Conflicting values " + v1 + " and " + v2);
},
tableSupplier);
} }
/** /**
@ -91,7 +98,26 @@ public final class Tables {
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction, BinaryOperator<V> mergeFunction,
java.util.function.Supplier<I> tableSupplier) { java.util.function.Supplier<I> tableSupplier) {
return TableCollectors.toTable(rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier); checkNotNull(rowFunction);
checkNotNull(columnFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
checkNotNull(tableSupplier);
return Collector.of(
tableSupplier,
(table, input) ->
merge(
table,
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(table1, table2) -> {
for (Table.Cell<R, C, V> cell2 : table2.cellSet()) {
merge(table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction);
}
return table1;
});
} }
private static <R, C, V> void merge( private static <R, C, V> void merge(

View file

@ -119,6 +119,7 @@ public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K,
@Override @Override
public void put(Range<K> range, V value) { public void put(Range<K> range, V value) {
// don't short-circuit if the range is empty - it may be between two ranges we can coalesce.
if (!range.isEmpty()) { if (!range.isEmpty()) {
checkNotNull(value); checkNotNull(value);
remove(range); remove(range);
@ -128,7 +129,6 @@ public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K,
@Override @Override
public void putCoalescing(Range<K> range, V value) { public void putCoalescing(Range<K> range, V value) {
// don't short-circuit if the range is empty - it may be between two ranges we can coalesce.
if (entriesByLowerBound.isEmpty()) { if (entriesByLowerBound.isEmpty()) {
put(range, value); put(range, value);
return; return;
@ -508,7 +508,7 @@ public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K,
@Override @Override
public void putCoalescing(Range<K> range, V value) { public void putCoalescing(Range<K> range, V value) {
if (entriesByLowerBound.isEmpty() || !subRange.encloses(range)) { if (entriesByLowerBound.isEmpty() || range.isEmpty() || !subRange.encloses(range)) {
put(range, value); put(range, value);
return; return;
} }

View file

@ -892,7 +892,7 @@ public class TreeRangeSet<C extends Comparable<?>> extends AbstractRangeSet<C>
"Cannot add range %s to subRangeSet(%s)", "Cannot add range %s to subRangeSet(%s)",
rangeToAdd, rangeToAdd,
restriction); restriction);
TreeRangeSet.this.add(rangeToAdd); super.add(rangeToAdd);
} }
@Override @Override

View file

@ -23,10 +23,9 @@ import java.lang.annotation.Target;
/** /**
* Marks a method as an event subscriber. * Marks a method as an event subscriber.
* *
* <p>The type of event will be indicated by the method's first (and only) parameter, which cannot * <p>The type of event will be indicated by the method's first (and only) parameter. If this
* be primitive. If this annotation is applied to methods with zero parameters, or more than one * annotation is applied to methods with zero parameters, or more than one parameter, the object
* parameter, the object containing the method will not be able to register for event delivery from * containing the method will not be able to register for event delivery from the {@link EventBus}.
* the {@link EventBus}.
* *
* <p>Unless also annotated with @{@link AllowConcurrentEvents}, event subscriber methods will be * <p>Unless also annotated with @{@link AllowConcurrentEvents}, event subscriber methods will be
* invoked serially by each event bus that they are registered with. * invoked serially by each event bus that they are registered with.

View file

@ -16,7 +16,6 @@ package com.google.common.eventbus;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.throwIfUnchecked;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
@ -32,7 +31,6 @@ import com.google.common.collect.Iterators;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.UncheckedExecutionException;
@ -172,12 +170,7 @@ final class SubscriberRegistry {
} }
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) { private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
try { return subscriberMethodsCache.getUnchecked(clazz);
return subscriberMethodsCache.getUnchecked(clazz);
} catch (UncheckedExecutionException e) {
throwIfUnchecked(e.getCause());
throw e;
}
} }
private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) { private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
@ -194,14 +187,7 @@ final class SubscriberRegistry {
+ "Subscriber methods must have exactly 1 parameter.", + "Subscriber methods must have exactly 1 parameter.",
method, method,
parameterTypes.length); parameterTypes.length);
checkArgument(
!parameterTypes[0].isPrimitive(),
"@Subscribe method %s's parameter is %s. "
+ "Subscriber methods cannot accept primitives. "
+ "Consider changing the parameter to %s.",
method,
parameterTypes[0].getName(),
Primitives.wrap(parameterTypes[0]).getSimpleName());
MethodIdentifier ident = new MethodIdentifier(method); MethodIdentifier ident = new MethodIdentifier(method);
if (!identifiers.containsKey(ident)) { if (!identifiers.containsKey(ident)) {
identifiers.put(ident, method); identifiers.put(ident, method);

View file

@ -44,7 +44,7 @@ import java.util.Set;
abstract class AbstractBaseGraph<N> implements BaseGraph<N> { abstract class AbstractBaseGraph<N> implements BaseGraph<N> {
/** /**
* Returns the number of edges in this graph; used to calculate the size of {@code #edges()}. This * Returns the number of edges in this graph; used to calculate the size of {@link #edges()}. This
* implementation requires O(|N|) time. Classes extending this one may manually keep track of the * implementation requires O(|N|) time. Classes extending this one may manually keep track of the
* number of edges as the graph is updated, and override this method for better performance. * number of edges as the graph is updated, and override this method for better performance.
*/ */
@ -59,7 +59,7 @@ abstract class AbstractBaseGraph<N> implements BaseGraph<N> {
} }
/** /**
* An implementation of {@link BaseGraph#edges()} defined in terms of {@code #nodes()} and {@link * An implementation of {@link BaseGraph#edges()} defined in terms of {@link #nodes()} and {@link
* #successors(Object)}. * #successors(Object)}.
*/ */
@Override @Override

View file

@ -22,10 +22,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import com.google.common.collect.AbstractIterator; import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.UnmodifiableIterator;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Queue;
import java.util.Set; import java.util.Set;
@ -60,11 +63,6 @@ import java.util.Set;
*/ */
@Beta @Beta
public abstract class Traverser<N> { public abstract class Traverser<N> {
private final SuccessorsFunction<N> successorFunction;
private Traverser(SuccessorsFunction<N> successorFunction) {
this.successorFunction = checkNotNull(successorFunction);
}
/** /**
* Creates a new traverser for the given general {@code graph}. * Creates a new traverser for the given general {@code graph}.
@ -90,14 +88,10 @@ public abstract class Traverser<N> {
* *
* @param graph {@link SuccessorsFunction} representing a general graph that may have cycles. * @param graph {@link SuccessorsFunction} representing a general graph that may have cycles.
*/ */
public static <N> Traverser<N> forGraph(final SuccessorsFunction<N> graph) { public static <N> Traverser<N> forGraph(SuccessorsFunction<N> graph) {
return new Traverser<N>(graph) { checkNotNull(graph);
@Override return new GraphTraverser<>(graph);
Traversal<N> newTraversal() { }
return Traversal.inGraph(graph);
}
};
}
/** /**
* Creates a new traverser for a directed acyclic graph that has at most one path from the start * Creates a new traverser for a directed acyclic graph that has at most one path from the start
@ -180,12 +174,7 @@ public abstract class Traverser<N> {
if (tree instanceof Network) { if (tree instanceof Network) {
checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees."); checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees.");
} }
return new Traverser<N>(tree) { return new TreeTraverser<>(tree);
@Override
Traversal<N> newTraversal() {
return Traversal.inTree(tree);
}
};
} }
/** /**
@ -219,9 +208,7 @@ public abstract class Traverser<N> {
* *
* @throws IllegalArgumentException if {@code startNode} is not an element of the graph * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
*/ */
public final Iterable<N> breadthFirst(N startNode) { public abstract Iterable<N> breadthFirst(N startNode);
return breadthFirst(ImmutableSet.of(startNode));
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
@ -233,15 +220,7 @@ public abstract class Traverser<N> {
* @see #breadthFirst(Object) * @see #breadthFirst(Object)
* @since 24.1 * @since 24.1
*/ */
public final Iterable<N> breadthFirst(Iterable<? extends N> startNodes) { public abstract Iterable<N> breadthFirst(Iterable<? extends N> startNodes);
final ImmutableSet<N> validated = validate(startNodes);
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return newTraversal().breadthFirst(validated.iterator());
}
};
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
@ -274,9 +253,7 @@ public abstract class Traverser<N> {
* *
* @throws IllegalArgumentException if {@code startNode} is not an element of the graph * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
*/ */
public final Iterable<N> depthFirstPreOrder(N startNode) { public abstract Iterable<N> depthFirstPreOrder(N startNode);
return depthFirstPreOrder(ImmutableSet.of(startNode));
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
@ -288,15 +265,7 @@ public abstract class Traverser<N> {
* @see #depthFirstPreOrder(Object) * @see #depthFirstPreOrder(Object)
* @since 24.1 * @since 24.1
*/ */
public final Iterable<N> depthFirstPreOrder(Iterable<? extends N> startNodes) { public abstract Iterable<N> depthFirstPreOrder(Iterable<? extends N> startNodes);
final ImmutableSet<N> validated = validate(startNodes);
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return newTraversal().preOrder(validated.iterator());
}
};
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
@ -329,9 +298,7 @@ public abstract class Traverser<N> {
* *
* @throws IllegalArgumentException if {@code startNode} is not an element of the graph * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
*/ */
public final Iterable<N> depthFirstPostOrder(N startNode) { public abstract Iterable<N> depthFirstPostOrder(N startNode);
return depthFirstPostOrder(ImmutableSet.of(startNode));
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
@ -343,156 +310,352 @@ public abstract class Traverser<N> {
* @see #depthFirstPostOrder(Object) * @see #depthFirstPostOrder(Object)
* @since 24.1 * @since 24.1
*/ */
public final Iterable<N> depthFirstPostOrder(Iterable<? extends N> startNodes) { public abstract Iterable<N> depthFirstPostOrder(Iterable<? extends N> startNodes);
final ImmutableSet<N> validated = validate(startNodes);
return new Iterable<N>() { // Avoid subclasses outside of this class
@Override private Traverser() {}
public Iterator<N> iterator() {
return newTraversal().postOrder(validated.iterator()); private static final class GraphTraverser<N> extends Traverser<N> {
private final SuccessorsFunction<N> graph;
GraphTraverser(SuccessorsFunction<N> graph) {
this.graph = checkNotNull(graph);
}
@Override
public Iterable<N> breadthFirst(final N startNode) {
checkNotNull(startNode);
return breadthFirst(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> breadthFirst(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
} }
}; for (N startNode : startNodes) {
} checkThatNodeIsInGraph(startNode);
}
abstract Traversal<N> newTraversal(); return new Iterable<N>() {
@SuppressWarnings("CheckReturnValue")
private ImmutableSet<N> validate(Iterable<? extends N> startNodes) {
ImmutableSet<N> copy = ImmutableSet.copyOf(startNodes);
for (N node : copy) {
successorFunction.successors(node); // Will throw if node doesn't exist
}
return copy;
}
/**
* Abstracts away the difference between traversing a graph vs. a tree. For a tree, we just take
* the next element from the next non-empty iterator; for graph, we need to loop through the next
* non-empty iterator to find first unvisited node.
*/
private abstract static class Traversal<N> {
final SuccessorsFunction<N> successorFunction;
Traversal(SuccessorsFunction<N> successorFunction) {
this.successorFunction = successorFunction;
}
static <N> Traversal<N> inGraph(SuccessorsFunction<N> graph) {
final Set<N> visited = new HashSet<>();
return new Traversal<N>(graph) {
@Override @Override
N visitNext(Deque<Iterator<? extends N>> horizon) { public Iterator<N> iterator() {
Iterator<? extends N> top = horizon.getFirst(); return new BreadthFirstIterator(startNodes);
while (top.hasNext()) { }
N element = checkNotNull(top.next()); };
if (visited.add(element)) { }
return element;
@Override
public Iterable<N> depthFirstPreOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPreOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPreOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInGraph(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstIterator(startNodes, Order.PREORDER);
}
};
}
@Override
public Iterable<N> depthFirstPostOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPostOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPostOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInGraph(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstIterator(startNodes, Order.POSTORDER);
}
};
}
@SuppressWarnings("CheckReturnValue")
private void checkThatNodeIsInGraph(N startNode) {
// successors() throws an IllegalArgumentException for nodes that are not an element of the
// graph.
graph.successors(startNode);
}
private final class BreadthFirstIterator extends UnmodifiableIterator<N> {
private final Queue<N> queue = new ArrayDeque<>();
private final Set<N> visited = new HashSet<>();
BreadthFirstIterator(Iterable<? extends N> roots) {
for (N root : roots) {
// add all roots to the queue, skipping duplicates
if (visited.add(root)) {
queue.add(root);
}
}
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
@Override
public N next() {
N current = queue.remove();
for (N neighbor : graph.successors(current)) {
if (visited.add(neighbor)) {
queue.add(neighbor);
}
}
return current;
}
}
private final class DepthFirstIterator extends AbstractIterator<N> {
private final Deque<NodeAndSuccessors> stack = new ArrayDeque<>();
private final Set<N> visited = new HashSet<>();
private final Order order;
DepthFirstIterator(Iterable<? extends N> roots, Order order) {
stack.push(new NodeAndSuccessors(null, roots));
this.order = order;
}
@Override
protected N computeNext() {
while (true) {
if (stack.isEmpty()) {
return endOfData();
}
NodeAndSuccessors nodeAndSuccessors = stack.getFirst();
boolean firstVisit = visited.add(nodeAndSuccessors.node);
boolean lastVisit = !nodeAndSuccessors.successorIterator.hasNext();
boolean produceNode =
(firstVisit && order == Order.PREORDER) || (lastVisit && order == Order.POSTORDER);
if (lastVisit) {
stack.pop();
} else {
// we need to push a neighbor, but only if we haven't already seen it
N successor = nodeAndSuccessors.successorIterator.next();
if (!visited.contains(successor)) {
stack.push(withSuccessors(successor));
} }
} }
horizon.removeFirst(); if (produceNode && nodeAndSuccessors.node != null) {
return null; return nodeAndSuccessors.node;
}
};
}
static <N> Traversal<N> inTree(SuccessorsFunction<N> tree) {
return new Traversal<N>(tree) {
@Override
N visitNext(Deque<Iterator<? extends N>> horizon) {
Iterator<? extends N> top = horizon.getFirst();
if (top.hasNext()) {
return checkNotNull(top.next());
} }
horizon.removeFirst();
return null;
} }
}; }
}
final Iterator<N> breadthFirst(Iterator<? extends N> startNodes) { NodeAndSuccessors withSuccessors(N node) {
return topDown(startNodes, InsertionOrder.BACK); return new NodeAndSuccessors(node, graph.successors(node));
} }
final Iterator<N> preOrder(Iterator<? extends N> startNodes) { /** A simple tuple of a node and a partially iterated {@link Iterator} of its successors. */
return topDown(startNodes, InsertionOrder.FRONT); private final class NodeAndSuccessors {
} final N node;
final Iterator<? extends N> successorIterator;
/** NodeAndSuccessors(N node, Iterable<? extends N> successors) {
* In top-down traversal, an ancestor node is always traversed before any of its descendant this.node = node;
* nodes. The traversal order among descendant nodes (particularly aunts and nieces) are this.successorIterator = successors.iterator();
* determined by the {@code InsertionOrder} parameter: nieces are placed at the FRONT before
* aunts for pre-order; while in BFS they are placed at the BACK after aunts.
*/
private Iterator<N> topDown(Iterator<? extends N> startNodes, final InsertionOrder order) {
final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>();
horizon.add(startNodes);
return new AbstractIterator<N>() {
@Override
protected N computeNext() {
do {
N next = visitNext(horizon);
if (next != null) {
Iterator<? extends N> successors = successorFunction.successors(next).iterator();
if (successors.hasNext()) {
// BFS: horizon.addLast(successors)
// Pre-order: horizon.addFirst(successors)
order.insertInto(horizon, successors);
}
return next;
}
} while (!horizon.isEmpty());
return endOfData();
} }
}; }
} }
final Iterator<N> postOrder(Iterator<? extends N> startNodes) {
final Deque<N> ancestorStack = new ArrayDeque<>();
final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>();
horizon.add(startNodes);
return new AbstractIterator<N>() {
@Override
protected N computeNext() {
for (N next = visitNext(horizon); next != null; next = visitNext(horizon)) {
Iterator<? extends N> successors = successorFunction.successors(next).iterator();
if (!successors.hasNext()) {
return next;
}
horizon.addFirst(successors);
ancestorStack.push(next);
}
return ancestorStack.isEmpty() ? endOfData() : ancestorStack.pop();
}
};
}
/**
* Visits the next node from the top iterator of {@code horizon} and returns the visited node.
* Null is returned to indicate reaching the end of the top iterator.
*
* <p>For example, if horizon is {@code [[a, b], [c, d], [e]]}, {@code visitNext()} will return
* {@code [a, b, null, c, d, null, e, null]} sequentially, encoding the topological structure.
* (Note, however, that the callers of {@code visitNext()} often insert additional iterators
* into {@code horizon} between calls to {@code visitNext()}. This causes them to receive
* additional values interleaved with those shown above.)
*/
abstract N visitNext(Deque<Iterator<? extends N>> horizon);
} }
/** Poor man's method reference for {@code Deque::addFirst} and {@code Deque::addLast}. */ private static final class TreeTraverser<N> extends Traverser<N> {
private enum InsertionOrder { private final SuccessorsFunction<N> tree;
FRONT {
@Override
<T> void insertInto(Deque<T> deque, T value) {
deque.addFirst(value);
}
},
BACK {
@Override
<T> void insertInto(Deque<T> deque, T value) {
deque.addLast(value);
}
};
abstract <T> void insertInto(Deque<T> deque, T value); TreeTraverser(SuccessorsFunction<N> tree) {
this.tree = checkNotNull(tree);
}
@Override
public Iterable<N> breadthFirst(final N startNode) {
checkNotNull(startNode);
return breadthFirst(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> breadthFirst(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInTree(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new BreadthFirstIterator(startNodes);
}
};
}
@Override
public Iterable<N> depthFirstPreOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPreOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPreOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N node : startNodes) {
checkThatNodeIsInTree(node);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstPreOrderIterator(startNodes);
}
};
}
@Override
public Iterable<N> depthFirstPostOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPostOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPostOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInTree(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstPostOrderIterator(startNodes);
}
};
}
@SuppressWarnings("CheckReturnValue")
private void checkThatNodeIsInTree(N startNode) {
// successors() throws an IllegalArgumentException for nodes that are not an element of the
// graph.
tree.successors(startNode);
}
private final class BreadthFirstIterator extends UnmodifiableIterator<N> {
private final Queue<N> queue = new ArrayDeque<>();
BreadthFirstIterator(Iterable<? extends N> roots) {
for (N root : roots) {
queue.add(root);
}
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
@Override
public N next() {
N current = queue.remove();
Iterables.addAll(queue, tree.successors(current));
return current;
}
}
private final class DepthFirstPreOrderIterator extends UnmodifiableIterator<N> {
private final Deque<Iterator<? extends N>> stack = new ArrayDeque<>();
DepthFirstPreOrderIterator(Iterable<? extends N> roots) {
stack.addLast(roots.iterator());
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public N next() {
Iterator<? extends N> iterator = stack.getLast(); // throws NoSuchElementException if empty
N result = checkNotNull(iterator.next());
if (!iterator.hasNext()) {
stack.removeLast();
}
Iterator<? extends N> childIterator = tree.successors(result).iterator();
if (childIterator.hasNext()) {
stack.addLast(childIterator);
}
return result;
}
}
private final class DepthFirstPostOrderIterator extends AbstractIterator<N> {
private final ArrayDeque<NodeAndChildren> stack = new ArrayDeque<>();
DepthFirstPostOrderIterator(Iterable<? extends N> roots) {
stack.addLast(new NodeAndChildren(null, roots));
}
@Override
protected N computeNext() {
while (!stack.isEmpty()) {
NodeAndChildren top = stack.getLast();
if (top.childIterator.hasNext()) {
N child = top.childIterator.next();
stack.addLast(withChildren(child));
} else {
stack.removeLast();
if (top.node != null) {
return top.node;
}
}
}
return endOfData();
}
NodeAndChildren withChildren(N node) {
return new NodeAndChildren(node, tree.successors(node));
}
/** A simple tuple of a node and a partially iterated {@link Iterator} of its children. */
private final class NodeAndChildren {
final N node;
final Iterator<? extends N> childIterator;
NodeAndChildren(N node, Iterable<? extends N> children) {
this.node = node;
this.childIterator = children.iterator();
}
}
}
}
private enum Order {
PREORDER,
POSTORDER
} }
} }

View file

@ -110,18 +110,18 @@ final class Crc32cHashFunction extends AbstractHashFunction {
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
}; };
private int crc = ~0; private int crc = 0;
@Override @Override
public void update(byte b) { public void update(byte b) {
crc ^= 0xFFFFFFFF; crc ^= 0xFFFFFFFF;
// See Hacker's Delight 2nd Edition, Figure 14-7. // See Hacker's Delight 2nd Edition, Figure 14-7.
crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ b) & 0xFF]; crc = ~((crc >>> 8) ^ CRC_TABLE[(crc ^ b) & 0xFF]);
} }
@Override @Override
public HashCode hash() { public HashCode hash() {
return HashCode.fromInt(~crc); return HashCode.fromInt(crc);
} }
} }
} }

View file

@ -39,7 +39,7 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Queue; import java.util.Deque;
/** /**
* Provides utility methods for working with byte arrays and I/O streams. * Provides utility methods for working with byte arrays and I/O streams.
@ -164,7 +164,7 @@ public final class ByteStreams {
* a total combined length of {@code totalLen} bytes) followed by all bytes remaining in the given * a total combined length of {@code totalLen} bytes) followed by all bytes remaining in the given
* input stream. * input stream.
*/ */
private static byte[] toByteArrayInternal(InputStream in, Queue<byte[]> bufs, int totalLen) private static byte[] toByteArrayInternal(InputStream in, Deque<byte[]> bufs, int totalLen)
throws IOException { throws IOException {
// Starting with an 8k buffer, double the size of each sucessive buffer. Buffers are retained // Starting with an 8k buffer, double the size of each sucessive buffer. Buffers are retained
// in a deque so that there's no copying between buffers while reading and so all of the bytes // in a deque so that there's no copying between buffers while reading and so all of the bytes
@ -195,11 +195,11 @@ public final class ByteStreams {
} }
} }
private static byte[] combineBuffers(Queue<byte[]> bufs, int totalLen) { private static byte[] combineBuffers(Deque<byte[]> bufs, int totalLen) {
byte[] result = new byte[totalLen]; byte[] result = new byte[totalLen];
int remaining = totalLen; int remaining = totalLen;
while (remaining > 0) { while (remaining > 0) {
byte[] buf = bufs.remove(); byte[] buf = bufs.removeFirst();
int bytesToCopy = Math.min(remaining, buf.length); int bytesToCopy = Math.min(remaining, buf.length);
int resultOffset = totalLen - remaining; int resultOffset = totalLen - remaining;
System.arraycopy(buf, 0, result, resultOffset, bytesToCopy); System.arraycopy(buf, 0, result, resultOffset, bytesToCopy);
@ -252,7 +252,7 @@ public final class ByteStreams {
} }
// the stream was longer, so read the rest normally // the stream was longer, so read the rest normally
Queue<byte[]> bufs = new ArrayDeque<byte[]>(TO_BYTE_ARRAY_DEQUE_SIZE + 2); Deque<byte[]> bufs = new ArrayDeque<byte[]>(TO_BYTE_ARRAY_DEQUE_SIZE + 2);
bufs.add(bytes); bufs.add(bytes);
bufs.add(new byte[] {(byte) b}); bufs.add(new byte[] {(byte) b});
return toByteArrayInternal(in, bufs, bytes.length + 1); return toByteArrayInternal(in, bufs, bytes.length + 1);

View file

@ -24,7 +24,6 @@ import com.google.common.base.Joiner;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.TreeTraverser; import com.google.common.collect.TreeTraverser;
@ -399,11 +398,6 @@ public final class Files {
* be exploited to create security vulnerabilities, especially when executable files are to be * be exploited to create security vulnerabilities, especially when executable files are to be
* written into the directory. * written into the directory.
* *
* <p>Depending on the environmment that this code is run in, the system temporary directory (and
* thus the directory this method creates) may be more visible that a program would like - files
* written to this directory may be read or overwritten by hostile programs running on the same
* machine.
*
* <p>This method assumes that the temporary volume is writable, has free inodes and free blocks, * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
* and that it will not be called thousands of times per second. * and that it will not be called thousands of times per second.
* *
@ -818,6 +812,36 @@ public final class Files {
return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
} }
/**
* Returns a {@link TreeTraverser} instance for {@link File} trees.
*
* <p><b>Warning:</b> {@code File} provides no support for symbolic links, and as such there is no
* way to ensure that a symbolic link to a directory is not followed when traversing the tree. In
* this case, iterables created by this traverser could contain files that are outside of the
* given directory or even be infinite if there is a symbolic link loop.
*
* @since 15.0
* @deprecated The returned {@link TreeTraverser} type is deprecated. Use the replacement method
* {@link #fileTraverser()} instead with the same semantics as this method.
*/
@Deprecated
static TreeTraverser<File> fileTreeTraverser() {
return FILE_TREE_TRAVERSER;
}
private static final TreeTraverser<File> FILE_TREE_TRAVERSER =
new TreeTraverser<File>() {
@Override
public Iterable<File> children(File file) {
return fileTreeChildren(file);
}
@Override
public String toString() {
return "Files.fileTreeTraverser()";
}
};
/** /**
* Returns a {@link Traverser} instance for the file and directory tree. The returned traverser * Returns a {@link Traverser} instance for the file and directory tree. The returned traverser
* starts from a {@link File} and will return all files and directories it encounters. * starts from a {@link File} and will return all files and directories it encounters.
@ -846,16 +870,12 @@ public final class Files {
} }
private static final SuccessorsFunction<File> FILE_TREE = private static final SuccessorsFunction<File> FILE_TREE =
file -> { new SuccessorsFunction<File>() {
// check isDirectory() just because it may be faster than listFiles() on a non-directory @Override
if (file.isDirectory()) { public Iterable<File> successors(File file) {
File[] files = file.listFiles(); return fileTreeChildren(file);
if (files != null) { }
return Collections.unmodifiableList(Arrays.asList(files)); };
}
}
return ImmutableList.of(); };
private static Iterable<File> fileTreeChildren(File file) { private static Iterable<File> fileTreeChildren(File file) {
// check isDirectory() just because it may be faster than listFiles() on a non-directory // check isDirectory() just because it may be faster than listFiles() on a non-directory

View file

@ -1,81 +0,0 @@
/*
* Copyright (C) 2020 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.math;
import com.google.common.annotations.GwtIncompatible;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* A class for arithmetic on {@link BigDecimal} that is not covered by its built-in methods.
*
* @author Louis Wasserman
* @since 30.0
*/
@GwtIncompatible
public class BigDecimalMath {
private BigDecimalMath() {}
/**
* Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
* is precisely representable as a {@code double}, its {@code double} value will be returned;
* otherwise, the rounding will choose between the two nearest representable values with {@code
* mode}.
*
* <p>For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN},
* infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not
* representable as a double, but {@code roundToDouble(BigDecimal.valueOf(2).pow(2000), HALF_UP)}
* will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}.
*
* <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
* default rounding mode: if the two nearest representable values are equally near, the one with
* the least significant bit zero is chosen. (In such cases, both of the nearest representable
* values are even integers; this method returns the one that is a multiple of a greater power of
* two.)
*
* @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
* is not precisely representable as a {@code double}
* @since 30.0
*/
public static double roundToDouble(BigDecimal x, RoundingMode mode) {
return BigDecimalToDoubleRounder.INSTANCE.roundToDouble(x, mode);
}
private static class BigDecimalToDoubleRounder extends ToDoubleRounder<BigDecimal> {
static final BigDecimalToDoubleRounder INSTANCE = new BigDecimalToDoubleRounder();
private BigDecimalToDoubleRounder() {}
@Override
double roundToDoubleArbitrarily(BigDecimal bigDecimal) {
return bigDecimal.doubleValue();
}
@Override
int sign(BigDecimal bigDecimal) {
return bigDecimal.signum();
}
@Override
BigDecimal toX(double d, RoundingMode mode) {
return new BigDecimal(d);
}
@Override
BigDecimal minus(BigDecimal a, BigDecimal b) {
return a.subtract(b);
}
}
}

View file

@ -21,7 +21,6 @@ import static com.google.common.math.MathPreconditions.checkPositive;
import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary; import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
import static java.math.RoundingMode.CEILING; import static java.math.RoundingMode.CEILING;
import static java.math.RoundingMode.FLOOR; import static java.math.RoundingMode.FLOOR;
import static java.math.RoundingMode.HALF_DOWN;
import static java.math.RoundingMode.HALF_EVEN; import static java.math.RoundingMode.HALF_EVEN;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
@ -57,7 +56,7 @@ public final class BigIntegerMath {
*/ */
@Beta @Beta
public static BigInteger ceilingPowerOfTwo(BigInteger x) { public static BigInteger ceilingPowerOfTwo(BigInteger x) {
return BigInteger.ZERO.setBit(log2(x, CEILING)); return BigInteger.ZERO.setBit(log2(x, RoundingMode.CEILING));
} }
/** /**
@ -69,7 +68,7 @@ public final class BigIntegerMath {
*/ */
@Beta @Beta
public static BigInteger floorPowerOfTwo(BigInteger x) { public static BigInteger floorPowerOfTwo(BigInteger x) {
return BigInteger.ZERO.setBit(log2(x, FLOOR)); return BigInteger.ZERO.setBit(log2(x, RoundingMode.FLOOR));
} }
/** Returns {@code true} if {@code x} represents a power of two. */ /** Returns {@code true} if {@code x} represents a power of two. */
@ -307,59 +306,6 @@ public final class BigIntegerMath {
return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), HALF_EVEN); return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), HALF_EVEN);
} }
/**
* Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
* is precisely representable as a {@code double}, its {@code double} value will be returned;
* otherwise, the rounding will choose between the two nearest representable values with {@code
* mode}.
*
* <p>For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN},
* infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not
* representable as a double, but {@code roundToDouble(BigInteger.valueOf(2).pow(2000), HALF_UP)}
* will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}.
*
* <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
* default rounding mode: if the two nearest representable values are equally near, the one with
* the least significant bit zero is chosen. (In such cases, both of the nearest representable
* values are even integers; this method returns the one that is a multiple of a greater power of
* two.)
*
* @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
* is not precisely representable as a {@code double}
* @since 30.0
*/
@GwtIncompatible
public static double roundToDouble(BigInteger x, RoundingMode mode) {
return BigIntegerToDoubleRounder.INSTANCE.roundToDouble(x, mode);
}
@GwtIncompatible
private static class BigIntegerToDoubleRounder extends ToDoubleRounder<BigInteger> {
static final BigIntegerToDoubleRounder INSTANCE = new BigIntegerToDoubleRounder();
private BigIntegerToDoubleRounder() {}
@Override
double roundToDoubleArbitrarily(BigInteger bigInteger) {
return DoubleUtils.bigToDouble(bigInteger);
}
@Override
int sign(BigInteger bigInteger) {
return bigInteger.signum();
}
@Override
BigInteger toX(double d, RoundingMode mode) {
return DoubleMath.roundToBigInteger(d, mode);
}
@Override
BigInteger minus(BigInteger a, BigInteger b) {
return a.subtract(b);
}
}
/** /**
* Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code * Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code
* RoundingMode}. * RoundingMode}.
@ -486,7 +432,7 @@ public final class BigIntegerMath {
long numeratorAccum = n; long numeratorAccum = n;
long denominatorAccum = 1; long denominatorAccum = 1;
int bits = LongMath.log2(n, CEILING); int bits = LongMath.log2(n, RoundingMode.CEILING);
int numeratorBits = bits; int numeratorBits = bits;

View file

@ -29,7 +29,6 @@ import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.primitives.Longs;
import com.google.common.primitives.UnsignedLongs; import com.google.common.primitives.UnsignedLongs;
import java.math.BigInteger; import java.math.BigInteger;
import java.math.RoundingMode; import java.math.RoundingMode;
@ -1005,30 +1004,10 @@ public final class LongMath {
checkNonNegative("n", n); checkNonNegative("n", n);
return false; return false;
} }
if (n < 66) { if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13) {
// Encode all primes less than 66 into mask without 0 and 1. return true;
long mask =
(1L << (2 - 2))
| (1L << (3 - 2))
| (1L << (5 - 2))
| (1L << (7 - 2))
| (1L << (11 - 2))
| (1L << (13 - 2))
| (1L << (17 - 2))
| (1L << (19 - 2))
| (1L << (23 - 2))
| (1L << (29 - 2))
| (1L << (31 - 2))
| (1L << (37 - 2))
| (1L << (41 - 2))
| (1L << (43 - 2))
| (1L << (47 - 2))
| (1L << (53 - 2))
| (1L << (59 - 2))
| (1L << (61 - 2));
// Look up n within the mask.
return ((mask >> ((int) n - 2)) & 1) != 0;
} }
if ((SIEVE_30 & (1 << (n % 30))) != 0) { if ((SIEVE_30 & (1 << (n % 30))) != 0) {
return false; return false;
} }
@ -1090,10 +1069,10 @@ public final class LongMath {
@Override @Override
long mulMod(long a, long b, long m) { long mulMod(long a, long b, long m) {
/* /*
* lowasser, 2015-Feb-12: Benchmarks suggest that changing this to UnsignedLongs.remainder * NOTE(lowasser, 2015-Feb-12): Benchmarks suggest that changing this to
* and increasing the threshold to 2^32 doesn't pay for itself, and adding another enum * UnsignedLongs.remainder and increasing the threshold to 2^32 doesn't pay for itself, and
* constant hurts performance further -- I suspect because bimorphic implementation is a * adding another enum constant hurts performance further -- I suspect because bimorphic
* sweet spot for the JVM. * implementation is a sweet spot for the JVM.
*/ */
return (a * b) % m; return (a * b) % m;
} }
@ -1224,125 +1203,5 @@ public final class LongMath {
} }
} }
/**
* Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
* is precisely representable as a {@code double}, its {@code double} value will be returned;
* otherwise, the rounding will choose between the two nearest representable values with {@code
* mode}.
*
* <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
* default rounding mode: if the two nearest representable values are equally near, the one with
* the least significant bit zero is chosen. (In such cases, both of the nearest representable
* values are even integers; this method returns the one that is a multiple of a greater power of
* two.)
*
* @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
* is not precisely representable as a {@code double}
* @since 30.0
*/
@SuppressWarnings("deprecation")
@GwtIncompatible
public static double roundToDouble(long x, RoundingMode mode) {
// Logic adapted from ToDoubleRounder.
double roundArbitrarily = (double) x;
long roundArbitrarilyAsLong = (long) roundArbitrarily;
int cmpXToRoundArbitrarily;
if (roundArbitrarilyAsLong == Long.MAX_VALUE) {
/*
* For most values, the conversion from roundArbitrarily to roundArbitrarilyAsLong is
* lossless. In that case we can compare x to roundArbitrarily using Longs.compare(x,
* roundArbitrarilyAsLong). The exception is for values where the conversion to double rounds
* up to give roundArbitrarily equal to 2^63, so the conversion back to long overflows and
* roundArbitrarilyAsLong is Long.MAX_VALUE. (This is the only way this condition can occur as
* otherwise the conversion back to long pads with zero bits.) In this case we know that
* roundArbitrarily > x. (This is important when x == Long.MAX_VALUE ==
* roundArbitrarilyAsLong.)
*/
cmpXToRoundArbitrarily = -1;
} else {
cmpXToRoundArbitrarily = Longs.compare(x, roundArbitrarilyAsLong);
}
switch (mode) {
case UNNECESSARY:
checkRoundingUnnecessary(cmpXToRoundArbitrarily == 0);
return roundArbitrarily;
case FLOOR:
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
case CEILING:
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
case DOWN:
if (x >= 0) {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
}
case UP:
if (x >= 0) {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
}
case HALF_DOWN:
case HALF_UP:
case HALF_EVEN:
{
long roundFloor;
double roundFloorAsDouble;
long roundCeiling;
double roundCeilingAsDouble;
if (cmpXToRoundArbitrarily >= 0) {
roundFloorAsDouble = roundArbitrarily;
roundFloor = roundArbitrarilyAsLong;
roundCeilingAsDouble = Math.nextUp(roundArbitrarily);
roundCeiling = (long) Math.ceil(roundCeilingAsDouble);
} else {
roundCeilingAsDouble = roundArbitrarily;
roundCeiling = roundArbitrarilyAsLong;
roundFloorAsDouble = DoubleUtils.nextDown(roundArbitrarily);
roundFloor = (long) Math.floor(roundFloorAsDouble);
}
long deltaToFloor = x - roundFloor;
long deltaToCeiling = roundCeiling - x;
if (roundCeiling == Long.MAX_VALUE) {
// correct for Long.MAX_VALUE as discussed above: roundCeilingAsDouble must be 2^63, but
// roundCeiling is 2^63-1.
deltaToCeiling++;
}
int diff = Longs.compare(deltaToFloor, deltaToCeiling);
if (diff < 0) { // closer to floor
return roundFloorAsDouble;
} else if (diff > 0) { // closer to ceiling
return roundCeilingAsDouble;
}
// halfway between the representable values; do the half-whatever logic
switch (mode) {
case HALF_EVEN:
return ((DoubleUtils.getSignificand(roundFloorAsDouble) & 1L) == 0)
? roundFloorAsDouble
: roundCeilingAsDouble;
case HALF_DOWN:
return (x >= 0) ? roundFloorAsDouble : roundCeilingAsDouble;
case HALF_UP:
return (x >= 0) ? roundCeilingAsDouble : roundFloorAsDouble;
default:
throw new AssertionError("impossible");
}
}
}
throw new AssertionError("impossible");
}
private LongMath() {} private LongMath() {}
} }

View file

@ -424,11 +424,11 @@ public final class Stats implements Serializable {
return false; return false;
} }
Stats other = (Stats) obj; Stats other = (Stats) obj;
return count == other.count return (count == other.count)
&& doubleToLongBits(mean) == doubleToLongBits(other.mean) && (doubleToLongBits(mean) == doubleToLongBits(other.mean))
&& doubleToLongBits(sumOfSquaresOfDeltas) == doubleToLongBits(other.sumOfSquaresOfDeltas) && (doubleToLongBits(sumOfSquaresOfDeltas) == doubleToLongBits(other.sumOfSquaresOfDeltas))
&& doubleToLongBits(min) == doubleToLongBits(other.min) && (doubleToLongBits(min) == doubleToLongBits(other.min))
&& doubleToLongBits(max) == doubleToLongBits(other.max); && (doubleToLongBits(max) == doubleToLongBits(other.max));
} }
/** /**

View file

@ -1,152 +0,0 @@
/*
* Copyright (C) 2020 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.math;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
import com.google.common.annotations.GwtIncompatible;
import java.math.RoundingMode;
/**
* Helper type to implement rounding {@code X} to a representable {@code double} value according to
* a {@link RoundingMode}.
*/
@GwtIncompatible
abstract class ToDoubleRounder<X extends Number & Comparable<X>> {
/**
* Returns x rounded to either the greatest double less than or equal to the precise value of x,
* or the least double greater than or equal to the precise value of x.
*/
abstract double roundToDoubleArbitrarily(X x);
/** Returns the sign of x: either -1, 0, or 1. */
abstract int sign(X x);
/** Returns d's value as an X, rounded with the specified mode. */
abstract X toX(double d, RoundingMode mode);
/** Returns a - b, guaranteed that both arguments are nonnegative. */
abstract X minus(X a, X b);
/** Rounds {@code x} to a {@code double}. */
final double roundToDouble(X x, RoundingMode mode) {
checkNotNull(x, "x");
checkNotNull(mode, "mode");
double roundArbitrarily = roundToDoubleArbitrarily(x);
if (Double.isInfinite(roundArbitrarily)) {
switch (mode) {
case DOWN:
case HALF_EVEN:
case HALF_DOWN:
case HALF_UP:
return Double.MAX_VALUE * sign(x);
case FLOOR:
return (roundArbitrarily == Double.POSITIVE_INFINITY)
? Double.MAX_VALUE
: Double.NEGATIVE_INFINITY;
case CEILING:
return (roundArbitrarily == Double.POSITIVE_INFINITY)
? Double.POSITIVE_INFINITY
: -Double.MAX_VALUE;
case UP:
return roundArbitrarily;
case UNNECESSARY:
throw new ArithmeticException(x + " cannot be represented precisely as a double");
}
}
X roundArbitrarilyAsX = toX(roundArbitrarily, RoundingMode.UNNECESSARY);
int cmpXToRoundArbitrarily = x.compareTo(roundArbitrarilyAsX);
switch (mode) {
case UNNECESSARY:
checkRoundingUnnecessary(cmpXToRoundArbitrarily == 0);
return roundArbitrarily;
case FLOOR:
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
case CEILING:
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
case DOWN:
if (sign(x) >= 0) {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
}
case UP:
if (sign(x) >= 0) {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
}
case HALF_DOWN:
case HALF_UP:
case HALF_EVEN:
{
X roundFloor;
double roundFloorAsDouble;
X roundCeiling;
double roundCeilingAsDouble;
if (cmpXToRoundArbitrarily >= 0) {
roundFloorAsDouble = roundArbitrarily;
roundFloor = roundArbitrarilyAsX;
roundCeilingAsDouble = Math.nextUp(roundArbitrarily);
if (roundCeilingAsDouble == Double.POSITIVE_INFINITY) {
return roundFloorAsDouble;
}
roundCeiling = toX(roundCeilingAsDouble, RoundingMode.CEILING);
} else {
roundCeilingAsDouble = roundArbitrarily;
roundCeiling = roundArbitrarilyAsX;
roundFloorAsDouble = DoubleUtils.nextDown(roundArbitrarily);
if (roundFloorAsDouble == Double.NEGATIVE_INFINITY) {
return roundCeilingAsDouble;
}
roundFloor = toX(roundFloorAsDouble, RoundingMode.FLOOR);
}
X deltaToFloor = minus(x, roundFloor);
X deltaToCeiling = minus(roundCeiling, x);
int diff = deltaToFloor.compareTo(deltaToCeiling);
if (diff < 0) { // closer to floor
return roundFloorAsDouble;
} else if (diff > 0) { // closer to ceiling
return roundCeilingAsDouble;
}
// halfway between the representable values; do the half-whatever logic
switch (mode) {
case HALF_EVEN:
// roundFloorAsDouble and roundCeilingAsDouble are neighbors, so precisely
// one of them should have an even long representation
return ((Double.doubleToRawLongBits(roundFloorAsDouble) & 1L) == 0)
? roundFloorAsDouble
: roundCeilingAsDouble;
case HALF_DOWN:
return (sign(x) >= 0) ? roundFloorAsDouble : roundCeilingAsDouble;
case HALF_UP:
return (sign(x) >= 0) ? roundCeilingAsDouble : roundFloorAsDouble;
default:
throw new AssertionError("impossible");
}
}
}
throw new AssertionError("impossible");
}
}

View file

@ -129,13 +129,6 @@ public final class HttpHeaders {
public static final String MAX_FORWARDS = "Max-Forwards"; public static final String MAX_FORWARDS = "Max-Forwards";
/** The HTTP {@code Origin} header field name. */ /** The HTTP {@code Origin} header field name. */
public static final String ORIGIN = "Origin"; public static final String ORIGIN = "Origin";
/**
* The HTTP <a href="https://github.com/WICG/origin-isolation">{@code Origin-Isolation}</a> header
* field name.
*
* @since 30.1
*/
public static final String ORIGIN_ISOLATION = "Origin-Isolation";
/** The HTTP {@code Proxy-Authorization} header field name. */ /** The HTTP {@code Proxy-Authorization} header field name. */
public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
/** The HTTP {@code Range} header field name. */ /** The HTTP {@code Range} header field name. */
@ -273,21 +266,6 @@ public final class HttpHeaders {
* @since 20.0 * @since 20.0
*/ */
public static final String X_WEBKIT_CSP_REPORT_ONLY = "X-WebKit-CSP-Report-Only"; public static final String X_WEBKIT_CSP_REPORT_ONLY = "X-WebKit-CSP-Report-Only";
/**
* The HTTP <a href="https://wicg.github.io/cross-origin-embedder-policy/#COEP">{@code
* Cross-Origin-Embedder-Policy}</a> header field name.
*
* @since 30.0
*/
public static final String CROSS_ORIGIN_EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy";
/**
* The HTTP <a href="https://wicg.github.io/cross-origin-embedder-policy/#COEP-RO">{@code
* Cross-Origin-Embedder-Policy-Report-Only}</a> header field name.
*
* @since 30.0
*/
public static final String CROSS_ORIGIN_EMBEDDER_POLICY_REPORT_ONLY =
"Cross-Origin-Embedder-Policy-Report-Only";
/** /**
* The HTTP Cross-Origin-Opener-Policy header field name. * The HTTP Cross-Origin-Opener-Policy header field name.
* *
@ -411,12 +389,6 @@ public final class HttpHeaders {
* @since 15.0 * @since 15.0
*/ */
@Beta public static final String PUBLIC_KEY_PINS = "Public-Key-Pins"; @Beta public static final String PUBLIC_KEY_PINS = "Public-Key-Pins";
/**
* The HTTP {@code X-Request-ID} header field name.
*
* @since 30.1
*/
public static final String X_REQUEST_ID = "X-Request-ID";
/** /**
* The HTTP <a href="http://tools.ietf.org/html/draft-evans-palmer-key-pinning">{@code * The HTTP <a href="http://tools.ietf.org/html/draft-evans-palmer-key-pinning">{@code
* Public-Key-Pins-Report-Only}</a> header field name. * Public-Key-Pins-Report-Only}</a> header field name.
@ -487,56 +459,6 @@ public final class HttpHeaders {
*/ */
public static final String X_MOZ = "X-Moz"; public static final String X_MOZ = "X-Moz";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-ua">{@code Sec-CH-UA}</a>
* header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA = "Sec-CH-UA";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-arch">{@code
* Sec-CH-UA-Arch}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_ARCH = "Sec-CH-UA-Arch";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-model">{@code
* Sec-CH-UA-Model}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_MODEL = "Sec-CH-UA-Model";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-platform">{@code
* Sec-CH-UA-Platform}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_PLATFORM = "Sec-CH-UA-Platform";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-platform-version">{@code
* Sec-CH-UA-Platform-Version}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_PLATFORM_VERSION = "Sec-CH-UA-Platform-Version";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-full-version">{@code
* Sec-CH-UA-Full-Version}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_FULL_VERSION = "Sec-CH-UA-Full-Version";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-mobile">{@code
* Sec-CH-UA-Mobile}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_MOBILE = "Sec-CH-UA-Mobile";
/** /**
* The HTTP <a href="https://w3c.github.io/webappsec-fetch-metadata/">{@code Sec-Fetch-Dest}</a> * The HTTP <a href="https://w3c.github.io/webappsec-fetch-metadata/">{@code Sec-Fetch-Dest}</a>
* header field name. * header field name.

View file

@ -19,9 +19,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.CharMatcher;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
@ -32,6 +32,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -103,10 +104,6 @@ public final class InetAddresses {
private static final int IPV6_PART_COUNT = 8; private static final int IPV6_PART_COUNT = 8;
private static final Splitter IPV4_SPLITTER = Splitter.on('.').limit(IPV4_PART_COUNT); private static final Splitter IPV4_SPLITTER = Splitter.on('.').limit(IPV4_PART_COUNT);
private static final Splitter IPV6_SPLITTER = Splitter.on(':').limit(IPV6_PART_COUNT + 2); private static final Splitter IPV6_SPLITTER = Splitter.on(':').limit(IPV6_PART_COUNT + 2);
private static final char IPV4_DELIMITER = '.';
private static final char IPV6_DELIMITER = ':';
private static final CharMatcher IPV4_DELIMITER_MATCHER = CharMatcher.is(IPV4_DELIMITER);
private static final CharMatcher IPV6_DELIMITER_MATCHER = CharMatcher.is(IPV6_DELIMITER);
private static final Inet4Address LOOPBACK4 = (Inet4Address) forString("127.0.0.1"); private static final Inet4Address LOOPBACK4 = (Inet4Address) forString("127.0.0.1");
private static final Inet4Address ANY4 = (Inet4Address) forString("0.0.0.0"); private static final Inet4Address ANY4 = (Inet4Address) forString("0.0.0.0");
@ -199,100 +196,81 @@ public final class InetAddresses {
} }
return textToNumericFormatV6(ipString); return textToNumericFormatV6(ipString);
} else if (hasDot) { } else if (hasDot) {
if (percentIndex != -1) {
return null; // Scope IDs are not supported for IPV4
}
return textToNumericFormatV4(ipString); return textToNumericFormatV4(ipString);
} }
return null; return null;
} }
private static byte [] textToNumericFormatV4(String ipString) { private static byte [] textToNumericFormatV4(String ipString) {
if (IPV4_DELIMITER_MATCHER.countIn(ipString) + 1 != IPV4_PART_COUNT) {
return null; // Wrong number of parts
}
byte[] bytes = new byte[IPV4_PART_COUNT]; byte[] bytes = new byte[IPV4_PART_COUNT];
int start = 0; int i = 0;
// Iterate through the parts of the ip string. try {
// Invariant: start is always the beginning of an octet. for (String octet : IPV4_SPLITTER.split(ipString)) {
for (int i = 0; i < IPV4_PART_COUNT; i++) { bytes[i++] = parseOctet(octet);
int end = ipString.indexOf(IPV4_DELIMITER, start);
if (end == -1) {
end = ipString.length();
} }
try { } catch (NumberFormatException ex) {
bytes[i] = parseOctet(ipString, start, end);
} catch (NumberFormatException ex) {
return null;
}
start = end + 1;
}
return bytes;
}
private static byte[] textToNumericFormatV6(String ipString) {
// An address can have [2..8] colons.
int delimiterCount = IPV6_DELIMITER_MATCHER.countIn(ipString);
if (delimiterCount < 2 || delimiterCount > IPV6_PART_COUNT) {
return null; return null;
} }
int partsSkipped = IPV6_PART_COUNT - (delimiterCount + 1); // estimate; may be modified later
boolean hasSkip = false; return i == IPV4_PART_COUNT ? bytes : null;
// Scan for the appearance of ::, to mark a skip-format IPV6 string and adjust the partsSkipped }
// estimate.
for (int i = 0; i < ipString.length() - 1; i++) { private static byte [] textToNumericFormatV6(String ipString) {
if (ipString.charAt(i) == IPV6_DELIMITER && ipString.charAt(i + 1) == IPV6_DELIMITER) { // An address can have [2..8] colons, and N colons make N+1 parts.
if (hasSkip) { List<String> parts = IPV6_SPLITTER.splitToList(ipString);
if (parts.size() < 3 || parts.size() > IPV6_PART_COUNT + 1) {
return null;
}
// Disregarding the endpoints, find "::" with nothing in between.
// This indicates that a run of zeroes has been skipped.
int skipIndex = -1;
for (int i = 1; i < parts.size() - 1; i++) {
if (parts.get(i).length() == 0) {
if (skipIndex >= 0) {
return null; // Can't have more than one :: return null; // Can't have more than one ::
} }
hasSkip = true; skipIndex = i;
partsSkipped++; // :: means we skipped an extra part in between the two delimiters.
if (i == 0) {
partsSkipped++; // Begins with ::, so we skipped the part preceding the first :
}
if (i == ipString.length() - 2) {
partsSkipped++; // Ends with ::, so we skipped the part after the last :
}
} }
} }
if (ipString.charAt(0) == IPV6_DELIMITER && ipString.charAt(1) != IPV6_DELIMITER) {
return null; // ^: requires ^:: int partsHi; // Number of parts to copy from above/before the "::"
} int partsLo; // Number of parts to copy from below/after the "::"
if (ipString.charAt(ipString.length() - 1) == IPV6_DELIMITER if (skipIndex >= 0) {
&& ipString.charAt(ipString.length() - 2) != IPV6_DELIMITER) { // If we found a "::", then check if it also covers the endpoints.
return null; // :$ requires ::$ partsHi = skipIndex;
} partsLo = parts.size() - skipIndex - 1;
if (hasSkip && partsSkipped <= 0) { if (parts.get(0).length() == 0 && --partsHi != 0) {
return null; // :: must expand to at least one '0' return null; // ^: requires ^::
} }
if (!hasSkip && delimiterCount + 1 != IPV6_PART_COUNT) { if (Iterables.getLast(parts).length() == 0 && --partsLo != 0) {
return null; // Incorrect number of parts return null; // :$ requires ::$
}
} else {
// Otherwise, allocate the entire address to partsHi. The endpoints
// could still be empty, but parseHextet() will check for that.
partsHi = parts.size();
partsLo = 0;
} }
// If we found a ::, then we must have skipped at least one part.
// Otherwise, we must have exactly the right number of parts.
int partsSkipped = IPV6_PART_COUNT - (partsHi + partsLo);
if (!(skipIndex >= 0 ? partsSkipped >= 1 : partsSkipped == 0)) {
return null;
}
// Now parse the hextets into a byte array.
ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT); ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT);
try { try {
// Iterate through the parts of the ip string. for (int i = 0; i < partsHi; i++) {
// Invariant: start is always the beginning of a hextet, or the second ':' of the skip rawBytes.putShort(parseHextet(parts.get(i)));
// sequence "::"
int start = 0;
if (ipString.charAt(0) == IPV6_DELIMITER) {
start = 1;
} }
while (start < ipString.length()) { for (int i = 0; i < partsSkipped; i++) {
int end = ipString.indexOf(IPV6_DELIMITER, start); rawBytes.putShort((short) 0);
if (end == -1) { }
end = ipString.length(); for (int i = partsLo; i > 0; i--) {
} rawBytes.putShort(parseHextet(parts.get(parts.size() - i)));
if (ipString.charAt(start) == IPV6_DELIMITER) {
// expand zeroes
for (int i = 0; i < partsSkipped; i++) {
rawBytes.putShort((short) 0);
}
} else {
rawBytes.putShort(parseHextet(ipString, start, end));
}
start = end + 1;
} }
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
return null; return null;
@ -313,45 +291,23 @@ public final class InetAddresses {
return initialPart + penultimate + ":" + ultimate; return initialPart + penultimate + ":" + ultimate;
} }
private static byte parseOctet(String ipString, int start, int end) { private static byte parseOctet(String ipPart) {
// Note: we already verified that this string contains only hex digits, but the string may still // Note: we already verified that this string contains only hex digits.
// contain non-decimal characters. int octet = Integer.parseInt(ipPart);
int length = end - start;
if (length <= 0 || length > 3) {
throw new NumberFormatException();
}
// Disallow leading zeroes, because no clear standard exists on // Disallow leading zeroes, because no clear standard exists on
// whether these should be interpreted as decimal or octal. // whether these should be interpreted as decimal or octal.
if (length > 1 && ipString.charAt(start) == '0') { if (octet > 255 || (ipPart.startsWith("0") && ipPart.length() > 1)) {
throw new NumberFormatException();
}
int octet = 0;
for (int i = start; i < end; i++) {
octet *= 10;
int digit = Character.digit(ipString.charAt(i), 10);
if (digit < 0) {
throw new NumberFormatException();
}
octet += digit;
}
if (octet > 255) {
throw new NumberFormatException(); throw new NumberFormatException();
} }
return (byte) octet; return (byte) octet;
} }
// Parse a hextet out of the ipString from start (inclusive) to end (exclusive) private static short parseHextet(String ipPart) {
private static short parseHextet(String ipString, int start, int end) {
// Note: we already verified that this string contains only hex digits. // Note: we already verified that this string contains only hex digits.
int length = end - start; int hextet = Integer.parseInt(ipPart, 16);
if (length <= 0 || length > 4) { if (hextet > 0xffff) {
throw new NumberFormatException(); throw new NumberFormatException();
} }
int hextet = 0;
for (int i = start; i < end; i++) {
hextet = hextet << 4;
hextet |= Character.digit(ipString.charAt(i), 16);
}
return (short) hextet; return (short) hextet;
} }

View file

@ -101,7 +101,6 @@ public final class MediaType {
private static final String IMAGE_TYPE = "image"; private static final String IMAGE_TYPE = "image";
private static final String TEXT_TYPE = "text"; private static final String TEXT_TYPE = "text";
private static final String VIDEO_TYPE = "video"; private static final String VIDEO_TYPE = "video";
private static final String FONT_TYPE = "font";
private static final String WILDCARD = "*"; private static final String WILDCARD = "*";
@ -141,7 +140,6 @@ public final class MediaType {
public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD); public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD);
public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD); public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD);
public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD); public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD);
public static final MediaType ANY_FONT_TYPE = createConstant(FONT_TYPE, WILDCARD);
/* text types */ /* text types */
public static final MediaType CACHE_MANIFEST_UTF_8 = public static final MediaType CACHE_MANIFEST_UTF_8 =
@ -697,60 +695,6 @@ public final class MediaType {
public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip");
/**
* A collection of font outlines as defined by <a href="https://tools.ietf.org/html/rfc8081">RFC
* 8081</a>.
*
* @since 30.0
*/
public static final MediaType FONT_COLLECTION = createConstant(FONT_TYPE, "collection");
/**
* <a href="https://en.wikipedia.org/wiki/OpenType">Open Type Font Format</a> (OTF) as defined by
* <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a>.
*
* @since 30.0
*/
public static final MediaType FONT_OTF = createConstant(FONT_TYPE, "otf");
/**
* <a href="https://en.wikipedia.org/wiki/SFNT">Spline or Scalable Font Format</a> (SFNT). <a
* href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct media
* type for SFNT, but {@link #SFNT application/font-sfnt} may be necessary in certain situations
* for compatibility.
*
* @since 30.0
*/
public static final MediaType FONT_SFNT = createConstant(FONT_TYPE, "sfnt");
/**
* <a href="https://en.wikipedia.org/wiki/TrueType">True Type Font Format</a> (TTF) as defined by
* <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a>.
*
* @since 30.0
*/
public static final MediaType FONT_TTF = createConstant(FONT_TYPE, "ttf");
/**
* <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font Format</a> (WOFF). <a
* href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct media
* type for SFNT, but {@link #WOFF application/font-woff} may be necessary in certain situations
* for compatibility.
*
* @since 30.0
*/
public static final MediaType FONT_WOFF = createConstant(FONT_TYPE, "woff");
/**
* <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font Format</a> (WOFF2).
* <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct
* media type for SFNT, but {@link #WOFF2 application/font-woff2} may be necessary in certain
* situations for compatibility.
*
* @since 30.0
*/
public static final MediaType FONT_WOFF2 = createConstant(FONT_TYPE, "woff2");
private final String type; private final String type;
private final String subtype; private final String subtype;
private final ImmutableListMultimap<String, String> parameters; private final ImmutableListMultimap<String, String> parameters;
@ -987,15 +931,6 @@ public final class MediaType {
return create(AUDIO_TYPE, subtype); return create(AUDIO_TYPE, subtype);
} }
/**
* Creates a media type with the "font" type and the given subtype.
*
* @throws IllegalArgumentException if subtype is invalid
*/
static MediaType createFontType(String subtype) {
return create(FONT_TYPE, subtype);
}
/** /**
* Creates a media type with the "image" type and the given subtype. * Creates a media type with the "image" type and the given subtype.
* *

View file

@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes; import static com.google.common.base.Preconditions.checkPositionIndexes;
import static com.google.common.base.Strings.lenientFormat;
import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NEGATIVE_INFINITY;
import static java.lang.Double.POSITIVE_INFINITY; import static java.lang.Double.POSITIVE_INFINITY;
@ -252,13 +251,8 @@ public final class Doubles {
*/ */
@Beta @Beta
public static double constrainToRange(double value, double min, double max) { public static double constrainToRange(double value, double min, double max) {
// avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984 checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max);
// Reject NaN by testing for the good case (min <= max) instead of the bad (min > max). return Math.min(Math.max(value, min), max);
if (min <= max) {
return Math.min(Math.max(value, min), max);
}
throw new IllegalArgumentException(
lenientFormat("min (%s) must be less than or equal to max (%s)", min, max));
} }
/** /**

View file

@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes; import static com.google.common.base.Preconditions.checkPositionIndexes;
import static com.google.common.base.Strings.lenientFormat;
import static java.lang.Float.NEGATIVE_INFINITY; import static java.lang.Float.NEGATIVE_INFINITY;
import static java.lang.Float.POSITIVE_INFINITY; import static java.lang.Float.POSITIVE_INFINITY;
@ -247,13 +246,8 @@ public final class Floats {
*/ */
@Beta @Beta
public static float constrainToRange(float value, float min, float max) { public static float constrainToRange(float value, float min, float max) {
// avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984 checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max);
// Reject NaN by testing for the good case (min <= max) instead of the bad (min > max). return Math.min(Math.max(value, min), max);
if (min <= max) {
return Math.min(Math.max(value, min), max);
}
throw new IllegalArgumentException(
lenientFormat("min (%s) must be less than or equal to max (%s)", min, max));
} }
/** /**

View file

@ -30,6 +30,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.io.ByteSource; import com.google.common.io.ByteSource;
import com.google.common.io.CharSource; import com.google.common.io.CharSource;
import com.google.common.io.Resources; import com.google.common.io.Resources;
@ -43,7 +46,7 @@ import java.nio.charset.Charset;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map.Entry;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.jar.Attributes; import java.util.jar.Attributes;
@ -75,6 +78,14 @@ import java.util.logging.Logger;
public final class ClassPath { public final class ClassPath {
private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); private static final Logger logger = Logger.getLogger(ClassPath.class.getName());
private static final Predicate<ClassInfo> IS_TOP_LEVEL =
new Predicate<ClassInfo>() {
@Override
public boolean apply(ClassInfo info) {
return info.className.indexOf('$') == -1;
}
};
/** Separator for the Class-Path manifest attribute value in jar files. */ /** Separator for the Class-Path manifest attribute value in jar files. */
private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR =
Splitter.on(" ").omitEmptyStrings(); Splitter.on(" ").omitEmptyStrings();
@ -104,21 +115,9 @@ public final class ClassPath {
* failed. * failed.
*/ */
public static ClassPath from(ClassLoader classloader) throws IOException { public static ClassPath from(ClassLoader classloader) throws IOException {
ImmutableSet<LocationInfo> locations = locationsFrom(classloader); DefaultScanner scanner = new DefaultScanner();
scanner.scan(classloader);
// Add all locations to the scanned set so that in a classpath [jar1, jar2], where jar1 has a return new ClassPath(scanner.getResources());
// manifest with Class-Path pointing to jar2, we won't scan jar2 twice.
Set<File> scanned = new HashSet<>();
for (LocationInfo location : locations) {
scanned.add(location.file());
}
// Scan all locations
ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder();
for (LocationInfo location : locations) {
builder.addAll(location.scanResources(scanned));
}
return new ClassPath(builder.build());
} }
/** /**
@ -138,20 +137,9 @@ public final class ClassPath {
return FluentIterable.from(resources).filter(ClassInfo.class).toSet(); return FluentIterable.from(resources).filter(ClassInfo.class).toSet();
} }
/** /** Returns all top level classes loadable from the current class path. */
* Returns all top level classes loadable from the current class path. Note that "top-level-ness" public ImmutableSet<ClassInfo> getTopLevelClasses() {
* is determined heuristically by class name (see {@link ClassInfo#isTopLevel}). return FluentIterable.from(resources).filter(ClassInfo.class).filter(IS_TOP_LEVEL).toSet();
*/ public ImmutableSet<ClassInfo> getTopLevelClasses() {
return FluentIterable.from(resources)
.filter(ClassInfo.class)
.filter(
new Predicate<ClassInfo>() {
@Override
public boolean apply(ClassInfo info) {
return info.isTopLevel();
}
})
.toSet();
} }
/** Returns all top level classes whose package name is {@code packageName}. */ /** Returns all top level classes whose package name is {@code packageName}. */
@ -190,21 +178,19 @@ public final class ClassPath {
*/ */
@Beta @Beta
public static class ResourceInfo { public static class ResourceInfo {
private final File file;
private final String resourceName; private final String resourceName;
final ClassLoader loader; final ClassLoader loader;
static ResourceInfo of(File file, String resourceName, ClassLoader loader) { static ResourceInfo of(String resourceName, ClassLoader loader) {
if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) { if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) {
return new ClassInfo(file, resourceName, loader); return new ClassInfo(resourceName, loader);
} else { } else {
return new ResourceInfo(file, resourceName, loader); return new ResourceInfo(resourceName, loader);
} }
} }
ResourceInfo(File file, String resourceName, ClassLoader loader) { ResourceInfo(String resourceName, ClassLoader loader) {
this.file = checkNotNull(file);
this.resourceName = checkNotNull(resourceName); this.resourceName = checkNotNull(resourceName);
this.loader = checkNotNull(loader); this.loader = checkNotNull(loader);
} }
@ -253,11 +239,6 @@ public final class ClassPath {
return resourceName; return resourceName;
} }
/** Returns the file that includes this resource. */
final File getFile() {
return file;
}
@Override @Override
public int hashCode() { public int hashCode() {
return resourceName.hashCode(); return resourceName.hashCode();
@ -288,8 +269,8 @@ public final class ClassPath {
public static final class ClassInfo extends ResourceInfo { public static final class ClassInfo extends ResourceInfo {
private final String className; private final String className;
ClassInfo(File file, String resourceName, ClassLoader loader) { ClassInfo(String resourceName, ClassLoader loader) {
super(file, resourceName, loader); super(resourceName, loader);
this.className = getClassName(resourceName); this.className = getClassName(resourceName);
} }
@ -336,18 +317,6 @@ public final class ClassPath {
return className; return className;
} }
/**
* Returns true if the class name "looks to be" top level (not nested), that is, it includes no
* '$' in the name. This method may return false for a top-level class that's intentionally
* named with the '$' character. If this is a concern, you could use {@link #load} and then
* check on the loaded {@link Class} object instead.
*
* @since 30.1
*/
public boolean isTopLevel() {
return className.indexOf('$') == -1;
}
/** /**
* Loads (but doesn't link or initialize) the class. * Loads (but doesn't link or initialize) the class.
* *
@ -369,66 +338,37 @@ public final class ClassPath {
} }
} }
/*
* Returns all locations that {@code classloader} and parent loaders load classes and resources
* from. Callers can {@linkplain LocationInfo#scanResources scan} individual locations selectively
* or even in parallel.
*/
static ImmutableSet<LocationInfo> locationsFrom(ClassLoader classloader) {
ImmutableSet.Builder<LocationInfo> builder = ImmutableSet.builder();
for (Map.Entry<File, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) {
builder.add(new LocationInfo(entry.getKey(), entry.getValue()));
}
return builder.build();
}
/** /**
* Represents a single location (a directory or a jar file) in the class path and is responsible * Abstract class that scans through the class path represented by a {@link ClassLoader} and calls
* for scanning resources from this location. * {@link #scanDirectory} and {@link #scanJarFile} for directories and jar files on the class path
* respectively.
*/ */
static final class LocationInfo { abstract static class Scanner {
final File home;
private final ClassLoader classloader;
LocationInfo(File home, ClassLoader classloader) { // We only scan each file once independent of the classloader that resource might be associated
this.home = checkNotNull(home); // with.
this.classloader = checkNotNull(classloader); private final Set<File> scannedUris = Sets.newHashSet();
public final void scan(ClassLoader classloader) throws IOException {
for (Entry<File, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) {
scan(entry.getKey(), entry.getValue());
}
} }
/** Returns the file this location is from. */ @VisibleForTesting
public final File file() { final void scan(File file, ClassLoader classloader) throws IOException {
return home; if (scannedUris.add(file.getCanonicalFile())) {
scanFrom(file, classloader);
}
} }
/** Scans this location and returns all scanned resources. */ /** Called when a directory is scanned for resource files. */
public ImmutableSet<ResourceInfo> scanResources() throws IOException { protected abstract void scanDirectory(ClassLoader loader, File directory) throws IOException;
return scanResources(new HashSet<File>());
}
/** /** Called when a jar file is scanned for resource entries. */
* Scans this location and returns all scanned resources. protected abstract void scanJarFile(ClassLoader loader, JarFile file) throws IOException;
*
* <p>This file and jar files from "Class-Path" entry in the scanned manifest files will be
* added to {@code scannedFiles}.
*
* <p>A file will be scanned at most once even if specified multiple times by one or multiple
* jar files' "Class-Path" manifest entries. Particularly, if a jar file from the "Class-Path"
* manifest entry is already in {@code scannedFiles}, either because it was scanned earlier, or
* it was intentionally added to the set by the caller, it will not be scanned again.
*
* <p>Note that when you call {@code location.scanResources(scannedFiles)}, the location will
* always be scanned even if {@code scannedFiles} already contains it.
*/
public ImmutableSet<ResourceInfo> scanResources(Set<File> scannedFiles) throws IOException {
ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder();
scannedFiles.add(home);
scan(home, scannedFiles, builder);
return builder.build();
}
private void scan(File file, Set<File> scannedUris, ImmutableSet.Builder<ResourceInfo> builder) private void scanFrom(File file, ClassLoader classloader) throws IOException {
throws IOException {
try { try {
if (!file.exists()) { if (!file.exists()) {
return; return;
@ -439,15 +379,13 @@ public final class ClassPath {
return; return;
} }
if (file.isDirectory()) { if (file.isDirectory()) {
scanDirectory(file, builder); scanDirectory(classloader, file);
} else { } else {
scanJar(file, scannedUris, builder); scanJar(file, classloader);
} }
} }
private void scanJar( private void scanJar(File file, ClassLoader classloader) throws IOException {
File file, Set<File> scannedUris, ImmutableSet.Builder<ResourceInfo> builder)
throws IOException {
JarFile jarFile; JarFile jarFile;
try { try {
jarFile = new JarFile(file); jarFile = new JarFile(file);
@ -457,37 +395,142 @@ public final class ClassPath {
} }
try { try {
for (File path : getClassPathFromManifest(file, jarFile.getManifest())) { for (File path : getClassPathFromManifest(file, jarFile.getManifest())) {
// We only scan each file once independent of the classloader that file might be scan(path, classloader);
// associated with.
if (scannedUris.add(path.getCanonicalFile())) {
scan(path, scannedUris, builder);
}
} }
scanJarFile(jarFile, builder); scanJarFile(classloader, jarFile);
} finally { } finally {
try { try {
jarFile.close(); jarFile.close();
} catch (IOException ignored) { // similar to try-with-resources, but don't fail scanning } catch (IOException ignored) {
} }
} }
} }
private void scanJarFile(JarFile file, ImmutableSet.Builder<ResourceInfo> builder) { /**
* Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
* to <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. If {@code manifest} is null, it means the jar file has no manifest,
* and an empty set will be returned.
*/
@VisibleForTesting
static ImmutableSet<File> getClassPathFromManifest(File jarFile, Manifest manifest) {
if (manifest == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder<File> builder = ImmutableSet.builder();
String classpathAttribute =
manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString());
if (classpathAttribute != null) {
for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
URL url;
try {
url = getClassPathEntry(jarFile, path);
} catch (MalformedURLException e) {
// Ignore bad entry
logger.warning("Invalid Class-Path entry: " + path);
continue;
}
if (url.getProtocol().equals("file")) {
builder.add(toFile(url));
}
}
}
return builder.build();
}
@VisibleForTesting
static ImmutableMap<File, ClassLoader> getClassPathEntries(ClassLoader classloader) {
LinkedHashMap<File, ClassLoader> entries = Maps.newLinkedHashMap();
// Search parent first, since it's the order ClassLoader#loadClass() uses.
ClassLoader parent = classloader.getParent();
if (parent != null) {
entries.putAll(getClassPathEntries(parent));
}
for (URL url : getClassLoaderUrls(classloader)) {
if (url.getProtocol().equals("file")) {
File file = toFile(url);
if (!entries.containsKey(file)) {
entries.put(file, classloader);
}
}
}
return ImmutableMap.copyOf(entries);
}
private static ImmutableList<URL> getClassLoaderUrls(ClassLoader classloader) {
if (classloader instanceof URLClassLoader) {
return ImmutableList.copyOf(((URLClassLoader) classloader).getURLs());
}
if (classloader.equals(ClassLoader.getSystemClassLoader())) {
return parseJavaClassPath();
}
return ImmutableList.of();
}
/**
* Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain
* System#getProperty system property}.
*/
@VisibleForTesting // TODO(b/65488446): Make this a public API.
static ImmutableList<URL> parseJavaClassPath() {
ImmutableList.Builder<URL> urls = ImmutableList.builder();
for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) {
try {
try {
urls.add(new File(entry).toURI().toURL());
} catch (SecurityException e) { // File.toURI checks to see if the file is a directory
urls.add(new URL("file", null, new File(entry).getAbsolutePath()));
}
} catch (MalformedURLException e) {
logger.log(WARNING, "malformed classpath entry: " + entry, e);
}
}
return urls.build();
}
/**
* Returns the absolute uri of the Class-Path entry value as specified in <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. Even though the specification only talks about relative urls,
* absolute urls are actually supported too (for example, in Maven surefire plugin).
*/
@VisibleForTesting
static URL getClassPathEntry(File jarFile, String path) throws MalformedURLException {
return new URL(jarFile.toURI().toURL(), path);
}
}
@VisibleForTesting
static final class DefaultScanner extends Scanner {
private final SetMultimap<ClassLoader, String> resources =
MultimapBuilder.hashKeys().linkedHashSetValues().build();
ImmutableSet<ResourceInfo> getResources() {
ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder();
for (Entry<ClassLoader, String> entry : resources.entries()) {
builder.add(ResourceInfo.of(entry.getValue(), entry.getKey()));
}
return builder.build();
}
@Override
protected void scanJarFile(ClassLoader classloader, JarFile file) {
Enumeration<JarEntry> entries = file.entries(); Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement(); JarEntry entry = entries.nextElement();
if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) { if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) {
continue; continue;
} }
builder.add(ResourceInfo.of(new File(file.getName()), entry.getName(), classloader)); resources.get(classloader).add(entry.getName());
} }
} }
private void scanDirectory(File directory, ImmutableSet.Builder<ResourceInfo> builder) @Override
throws IOException { protected void scanDirectory(ClassLoader classloader, File directory) throws IOException {
Set<File> currentPath = new HashSet<>(); Set<File> currentPath = new HashSet<>();
currentPath.add(directory.getCanonicalFile()); currentPath.add(directory.getCanonicalFile());
scanDirectory(directory, "", currentPath, builder); scanDirectory(directory, classloader, "", currentPath);
} }
/** /**
@ -496,17 +539,15 @@ public final class ClassPath {
* cycles; otherwise symlinks are traversed. * cycles; otherwise symlinks are traversed.
* *
* @param directory the root of the directory to scan * @param directory the root of the directory to scan
* @param classloader the classloader that includes resources found in {@code directory}
* @param packagePrefix resource path prefix inside {@code classloader} for any files found * @param packagePrefix resource path prefix inside {@code classloader} for any files found
* under {@code directory} * under {@code directory}
* @param currentPath canonical files already visited in the current directory tree path, for * @param currentPath canonical files already visited in the current directory tree path, for
* cycle elimination * cycle elimination
*/ */
private void scanDirectory( private void scanDirectory(
File directory, File directory, ClassLoader classloader, String packagePrefix, Set<File> currentPath)
String packagePrefix, throws IOException {
Set<File> currentPath,
ImmutableSet.Builder<ResourceInfo> builder)
throws IOException {
File[] files = directory.listFiles(); File[] files = directory.listFiles();
if (files == null) { if (files == null) {
logger.warning("Cannot read directory " + directory); logger.warning("Cannot read directory " + directory);
@ -518,132 +559,17 @@ public final class ClassPath {
if (f.isDirectory()) { if (f.isDirectory()) {
File deref = f.getCanonicalFile(); File deref = f.getCanonicalFile();
if (currentPath.add(deref)) { if (currentPath.add(deref)) {
scanDirectory(deref, packagePrefix + name + "/", currentPath, builder); scanDirectory(deref, classloader, packagePrefix + name + "/", currentPath);
currentPath.remove(deref); currentPath.remove(deref);
} }
} else { } else {
String resourceName = packagePrefix + name; String resourceName = packagePrefix + name;
if (!resourceName.equals(JarFile.MANIFEST_NAME)) { if (!resourceName.equals(JarFile.MANIFEST_NAME)) {
builder.add(ResourceInfo.of(f, resourceName, classloader)); resources.get(classloader).add(resourceName);
} }
} }
} }
} }
@Override
public boolean equals(Object obj) {
if (obj instanceof LocationInfo) {
LocationInfo that = (LocationInfo) obj;
return home.equals(that.home) && classloader.equals(that.classloader);
}
return false;
}
@Override
public int hashCode() {
return home.hashCode();
}
@Override
public String toString() {
return home.toString();
}
}
/**
* Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
* to <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. If {@code manifest} is null, it means the jar file has no manifest, and
* an empty set will be returned.
*/
@VisibleForTesting
static ImmutableSet<File> getClassPathFromManifest(File jarFile, Manifest manifest) {
if (manifest == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder<File> builder = ImmutableSet.builder();
String classpathAttribute =
manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString());
if (classpathAttribute != null) {
for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
URL url;
try {
url = getClassPathEntry(jarFile, path);
} catch (MalformedURLException e) {
// Ignore bad entry
logger.warning("Invalid Class-Path entry: " + path);
continue;
}
if (url.getProtocol().equals("file")) {
builder.add(toFile(url));
}
}
}
return builder.build();
}
@VisibleForTesting
static ImmutableMap<File, ClassLoader> getClassPathEntries(ClassLoader classloader) {
LinkedHashMap<File, ClassLoader> entries = Maps.newLinkedHashMap();
// Search parent first, since it's the order ClassLoader#loadClass() uses.
ClassLoader parent = classloader.getParent();
if (parent != null) {
entries.putAll(getClassPathEntries(parent));
}
for (URL url : getClassLoaderUrls(classloader)) {
if (url.getProtocol().equals("file")) {
File file = toFile(url);
if (!entries.containsKey(file)) {
entries.put(file, classloader);
}
}
}
return ImmutableMap.copyOf(entries);
}
private static ImmutableList<URL> getClassLoaderUrls(ClassLoader classloader) {
if (classloader instanceof URLClassLoader) {
return ImmutableList.copyOf(((URLClassLoader) classloader).getURLs());
}
if (classloader.equals(ClassLoader.getSystemClassLoader())) {
return parseJavaClassPath();
}
return ImmutableList.of();
}
/**
* Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain
* System#getProperty system property}.
*/
@VisibleForTesting // TODO(b/65488446): Make this a public API.
static ImmutableList<URL> parseJavaClassPath() {
ImmutableList.Builder<URL> urls = ImmutableList.builder();
for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) {
try {
try {
urls.add(new File(entry).toURI().toURL());
} catch (SecurityException e) { // File.toURI checks to see if the file is a directory
urls.add(new URL("file", null, new File(entry).getAbsolutePath()));
}
} catch (MalformedURLException e) {
logger.log(WARNING, "malformed classpath entry: " + entry, e);
}
}
return urls.build();
}
/**
* Returns the absolute uri of the Class-Path entry value as specified in <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. Even though the specification only talks about relative urls, absolute
* urls are actually supported too (for example, in Maven surefire plugin).
*/
@VisibleForTesting
static URL getClassPathEntry(File jarFile, String path) throws MalformedURLException {
return new URL(jarFile.toURI().toURL(), path);
} }
@VisibleForTesting @VisibleForTesting

View file

@ -33,8 +33,6 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.Integer.toHexString;
import static java.lang.System.identityHashCode;
import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;
/** /**
@ -370,6 +368,8 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* *
* <p>The default {@link AbstractFuture} implementation throws {@code InterruptedException} if the * <p>The default {@link AbstractFuture} implementation throws {@code InterruptedException} if the
* current thread is interrupted during the call, even if the value is already available. * current thread is interrupted during the call, even if the value is already available.
*
* @throws CancellationException {@inheritDoc}
*/ */
@Override @Override
public V get(long timeout, TimeUnit unit) public V get(long timeout, TimeUnit unit)
@ -396,7 +396,7 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
node.setNext(oldHead); node.setNext(oldHead);
if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) { if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) {
while (true) { while (true) {
OverflowAvoidingLockSupport.parkNanos(this, remainingNanos); LockSupport.parkNanos(this, remainingNanos);
// Check interruption first, if we woke up due to interruption we need to honor that. // Check interruption first, if we woke up due to interruption we need to honor that.
if (Thread.interrupted()) { if (Thread.interrupted()) {
removeWaiter(node); removeWaiter(node);
@ -478,6 +478,8 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* *
* <p>The default {@link AbstractFuture} implementation throws {@code InterruptedException} if the * <p>The default {@link AbstractFuture} implementation throws {@code InterruptedException} if the
* current thread is interrupted during the call, even if the value is already available. * current thread is interrupted during the call, even if the value is already available.
*
* @throws CancellationException {@inheritDoc}
*/ */
@Override @Override
public V get() throws InterruptedException, ExecutionException { public V get() throws InterruptedException, ExecutionException {
@ -559,8 +561,6 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* #wasInterrupted} as necessary. This ensures that the work is done even if the future is * #wasInterrupted} as necessary. This ensures that the work is done even if the future is
* cancelled without a call to {@code cancel}, such as by calling {@code * cancelled without a call to {@code cancel}, such as by calling {@code
* setFuture(cancelledFuture)}. * setFuture(cancelledFuture)}.
* <p>Beware of completing a future while holding a lock. Its listeners may do slow work or
* acquire other locks, risking deadlocks.
*/ */
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public boolean cancel(boolean mayInterruptIfRunning) {
@ -580,7 +580,7 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
while (true) { while (true) {
if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) { if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) {
rValue = true; rValue = true;
// We call interruptTask before calling complete(), which is consistent with // We call interuptTask before calling complete(), which is consistent with
// FutureTask // FutureTask
if (mayInterruptIfRunning) { if (mayInterruptIfRunning) {
abstractFuture.interruptTask(); abstractFuture.interruptTask();
@ -714,9 +714,6 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* known yet. That result, though not yet known, cannot be overridden by a call to a {@code set*} * known yet. That result, though not yet known, cannot be overridden by a call to a {@code set*}
* method, only by a call to {@link #cancel}. * method, only by a call to {@link #cancel}.
* *
* <p>Beware of completing a future while holding a lock. Its listeners may do slow work or
* acquire other locks, risking deadlocks.
*
* @param throwable the exception to be used as the failed result * @param throwable the exception to be used as the failed result
* @return true if the attempt was accepted, completing the {@code Future} * @return true if the attempt was accepted, completing the {@code Future}
*/ */
@ -750,9 +747,6 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* invoke the {@link #interruptTask} method, and the {@link #wasInterrupted} method will not * invoke the {@link #interruptTask} method, and the {@link #wasInterrupted} method will not
* return {@code true}. * return {@code true}.
* *
* <p>Beware of completing a future while holding a lock. Its listeners may do slow work or
* acquire other locks, risking deadlocks.
*
* @param future the future to delegate to * @param future the future to delegate to
* @return true if the attempt was accepted, indicating that the {@code Future} was not previously * @return true if the attempt was accepted, indicating that the {@code Future} was not previously
* cancelled or set. * cancelled or set.
@ -1037,14 +1031,7 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
// TODO(user): move parts into a default method on ListenableFuture? // TODO(user): move parts into a default method on ListenableFuture?
@Override @Override
public String toString() { public String toString() {
// TODO(cpovirk): Presize to something plausible? StringBuilder builder = new StringBuilder().append(super.toString()).append("[status=");
StringBuilder builder = new StringBuilder();
if (getClass().getName().startsWith("com.google.common.util.concurrent.")) {
builder.append(getClass().getSimpleName());
} else {
builder.append(getClass().getName());
}
builder.append('@').append(toHexString(identityHashCode(this))).append("[status=");
if (isCancelled()) { if (isCancelled()) {
builder.append("CANCELLED"); builder.append("CANCELLED");
} else if (isDone()) { } else if (isDone()) {
@ -1092,8 +1079,7 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
private void addDoneString(StringBuilder builder) { private void addDoneString(StringBuilder builder) {
try { try {
V value = getUninterruptibly(this); V value = getUninterruptibly(this);
builder.append("SUCCESS, result=["); builder.append("SUCCESS, result=[").append(userObjectToString(value)).append("]");
appendResultObject(builder, value);
} catch (ExecutionException e) { } catch (ExecutionException e) {
builder.append("FAILURE, cause=[").append(e.getCause()).append("]"); builder.append("FAILURE, cause=[").append(e.getCause()).append("]");
} catch (CancellationException e) { } catch (CancellationException e) {
@ -1103,24 +1089,6 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
} }
} }
/**
* Any object can be the result of a Future, and not every object has a reasonable toString()
* implementation. Using a reconstruction of the default Object.toString() prevents OOMs and stack
* overflows, and helps avoid sensitive data inadvertently ending up in exception messages.
*/
private void appendResultObject(StringBuilder builder, Object o) {
if (o == null) {
builder.append("null");
} else if (o == this) {
builder.append("this future");
} else {
builder
.append(o.getClass().getName())
.append("@")
.append(Integer.toHexString(System.identityHashCode(o)));
}
}
/** Helper for printing user supplied objects into our toString method. */ /** Helper for printing user supplied objects into our toString method. */
private String userObjectToString(Object o) { private String userObjectToString(Object o) {
// This is some basic recursion detection for when people create cycles via set/setFuture // This is some basic recursion detection for when people create cycles via set/setFuture

View file

@ -89,6 +89,7 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
private abstract class CombinedFutureInterruptibleTask<T> extends InterruptibleTask<T> { private abstract class CombinedFutureInterruptibleTask<T> extends InterruptibleTask<T> {
private final Executor listenerExecutor; private final Executor listenerExecutor;
boolean thrownByExecute = true;
CombinedFutureInterruptibleTask(Executor listenerExecutor) { CombinedFutureInterruptibleTask(Executor listenerExecutor) {
this.listenerExecutor = checkNotNull(listenerExecutor); this.listenerExecutor = checkNotNull(listenerExecutor);
@ -103,7 +104,9 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
try { try {
listenerExecutor.execute(this); listenerExecutor.execute(this);
} catch (RejectedExecutionException e) { } catch (RejectedExecutionException e) {
CombinedFuture.this.setException(e); if (thrownByExecute) {
CombinedFuture.this.setException(e);
}
} }
} }
@ -150,6 +153,7 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
@Override @Override
ListenableFuture<V> runInterruptibly() throws Exception { ListenableFuture<V> runInterruptibly() throws Exception {
thrownByExecute = false;
ListenableFuture<V> result = callable.call(); ListenableFuture<V> result = callable.call();
return checkNotNull( return checkNotNull(
result, result,
@ -180,6 +184,7 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
@Override @Override
V runInterruptibly() throws Exception { V runInterruptibly() throws Exception {
thrownByExecute = false;
return callable.call(); return callable.call();
} }

View file

@ -15,13 +15,11 @@
package com.google.common.util.concurrent; package com.google.common.util.concurrent;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.CANCELLED; import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.CANCELLED;
import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.NOT_RUN; import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.NOT_RUN;
import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.STARTED; import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.STARTED;
import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; import static com.google.common.util.concurrent.Futures.immediateCancelledFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
@ -30,50 +28,13 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
* Serializes execution of tasks, somewhat like an "asynchronous {@code synchronized} block." Each * Serializes execution of a set of operations. This class guarantees that a submitted callable will
* {@linkplain #submit enqueued} callable will not be submitted to its associated executor until the * not be called before previously submitted callables (and any {@code Future}s returned from them)
* previous callable has returned -- and, if the previous callable was an {@link AsyncCallable}, not * have completed.
* until the {@code Future} it returned is {@linkplain java.util.concurrent.Future#isDone done} (successful, failed, or
* cancelled).
* *
* <p>This class has limited support for cancellation and other "early completion": * <p>This class implements a superset of the behavior of {@link
* * MoreExecutors#newSequentialExecutor}. If your tasks all run on the same underlying executor and
* <ul> * don't need to wait for {@code Future}s returned from {@code AsyncCallable}s, use it instead.
* <li>While calls to {@code submit} and {@code submitAsync} return a {@code Future} that can be
* cancelled, cancellation never propagates to a task that has started to run -- neither to
* the callable itself nor to any {@code Future} returned by an {@code AsyncCallable}.
* (However, cancellation can prevent an <i>unstarted</i> task from running.) Therefore, the
* next task will wait for any running callable (or pending {@code Future} returned by an
* {@code AsyncCallable}) to complete, without interrupting it (and without calling {@code
* cancel} on the {@code Future}). So beware: <i>Even if you cancel every precededing {@code
* Future} returned by this class, the next task may still have to wait.</i>.
* <li>Once an {@code AsyncCallable} returns a {@code Future}, this class considers that task to
* be "done" as soon as <i>that</i> {@code Future} completes in any way. Notably, a {@code
* Future} is "completed" even if it is cancelled while its underlying work continues on a
* thread, an RPC, etc. The {@code Future} is also "completed" if it fails "early" -- for
* example, if the deadline expires on a {@code Future} returned from {@link
* Futures#withTimeout} while the {@code Future} it wraps continues its underlying work. So
* beware: <i>Your {@code AsyncCallable} should not complete its {@code Future} until it is
* safe for the next task to start.</i>
* </ul>
*
* <p>An additional limitation: this class serializes execution of <i>tasks</i> but not any
* <i>listeners</i> of those tasks.
*
* <p>This class is similar to {@link MoreExecutors#newSequentialExecutor}. This class is different
* in a few ways:
*
* <ul>
* <li>Each task may be associated with a different executor.
* <li>Tasks may be of type {@code AsyncCallable}.
* <li>Running tasks <i>cannot</i> be interrupted. (Note that {@code newSequentialExecutor} does
* not return {@code Future} objects, so it doesn't support interruption directly, either.
* However, utilities that <i>use</i> that executor have the ability to interrupt tasks
* running on it. This class, by contrast, does not expose an {@code Executor} API.)
* </ul>
*
* <p>If you don't need the features of this class, you may prefer {@code newSequentialExecutor} for
* its simplicity and ability to accommodate interruption.
* *
* @since 26.0 * @since 26.0
*/ */
@ -87,48 +48,16 @@ public final class ExecutionSequencer {
return new ExecutionSequencer(); return new ExecutionSequencer();
} }
/** This reference acts as a pointer tracking the head of a linked list of ListenableFutures. */ enum RunningState {
private final AtomicReference<ListenableFuture<Void>> ref = NOT_RUN,
new AtomicReference<>(immediateVoidFuture()); CANCELLED,
STARTED,
private ThreadConfinedTaskQueue latestTaskQueue = new ThreadConfinedTaskQueue();
/**
* This object is unsafely published, but avoids problematic races by relying exclusively on the
* identity equality of its Thread field so that the task field is only accessed by a single
* thread.
*/
private static final class ThreadConfinedTaskQueue {
/**
* This field is only used for identity comparisons with the current thread. Field assignments
* are atomic, but do not provide happens-before ordering; however:
*
* <ul>
* <li>If this field's value == currentThread, we know that it's up to date, because write
* operations in a thread always happen-before subsequent read operations in the same
* thread
* <li>If this field's value == null because of unsafe publication, we know that it isn't the
* object associated with our thread, because if it was the publication wouldn't have been
* unsafe and we'd have seen our thread as the value. This state is also why a new
* ThreadConfinedTaskQueue object must be created for each inline execution, because
* observing a null thread does not mean the object is safe to reuse.
* <li>If this field's value is some other thread object, we know that it's not our thread.
* <li>If this field's value == null because it originally belonged to another thread and that
* thread cleared it, we still know that it's not associated with our thread
* <li>If this field's value == null because it was associated with our thread and was
* cleared, we know that we're not executing inline any more
* </ul>
*
* All the states where thread != currentThread are identical for our purposes, and so even
* though it's racy, we don't care which of those values we get, so no need to synchronize.
*/
Thread thread;
/** Only used by the thread associated with this object */
Runnable nextTask;
/** Only used by the thread associated with this object */
Executor nextExecutor;
} }
/** This reference acts as a pointer tracking the head of a linked list of ListenableFutures. */
private final AtomicReference<ListenableFuture<Object>> ref =
new AtomicReference<>(immediateFuture(null));
/** /**
* Enqueues a task to run when the previous task (if any) completes. * Enqueues a task to run when the previous task (if any) completes.
* *
@ -138,7 +67,6 @@ public final class ExecutionSequencer {
*/ */
public <T> ListenableFuture<T> submit(final Callable<T> callable, Executor executor) { public <T> ListenableFuture<T> submit(final Callable<T> callable, Executor executor) {
checkNotNull(callable); checkNotNull(callable);
checkNotNull(executor);
return submitAsync( return submitAsync(
new AsyncCallable<T>() { new AsyncCallable<T>() {
@Override @Override
@ -164,13 +92,12 @@ public final class ExecutionSequencer {
public <T> ListenableFuture<T> submitAsync( public <T> ListenableFuture<T> submitAsync(
final AsyncCallable<T> callable, final Executor executor) { final AsyncCallable<T> callable, final Executor executor) {
checkNotNull(callable); checkNotNull(callable);
checkNotNull(executor); final AtomicReference<RunningState> runningState = new AtomicReference<>(NOT_RUN);
final TaskNonReentrantExecutor taskExecutor = new TaskNonReentrantExecutor(executor, this);
final AsyncCallable<T> task = final AsyncCallable<T> task =
new AsyncCallable<T>() { new AsyncCallable<T>() {
@Override @Override
public ListenableFuture<T> call() throws Exception { public ListenableFuture<T> call() throws Exception {
if (!taskExecutor.trySetStarted()) { if (!runningState.compareAndSet(NOT_RUN, STARTED)) {
return immediateCancelledFuture(); return immediateCancelledFuture();
} }
return callable.call(); return callable.call();
@ -192,13 +119,20 @@ public final class ExecutionSequencer {
* have completed - namely after oldFuture is done, and taskFuture has either completed or been * have completed - namely after oldFuture is done, and taskFuture has either completed or been
* cancelled before the callable started execution. * cancelled before the callable started execution.
*/ */
final SettableFuture<Void> newFuture = SettableFuture.create(); final SettableFuture<Object> newFuture = SettableFuture.create();
final ListenableFuture<Void> oldFuture = ref.getAndSet(newFuture); final ListenableFuture<?> oldFuture = ref.getAndSet(newFuture);
// Invoke our task once the previous future completes. // Invoke our task once the previous future completes.
final TrustedListenableFutureTask<T> taskFuture = TrustedListenableFutureTask.create(task); final ListenableFuture<T> taskFuture =
oldFuture.addListener(taskFuture, taskExecutor); Futures.submitAsync(
task,
new Executor() {
@Override
public void execute(Runnable runnable) {
oldFuture.addListener(runnable, executor);
}
});
final ListenableFuture<T> outputFuture = Futures.nonCancellationPropagating(taskFuture); final ListenableFuture<T> outputFuture = Futures.nonCancellationPropagating(taskFuture);
@ -210,39 +144,15 @@ public final class ExecutionSequencer {
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
if (taskFuture.isDone()) { if (taskFuture.isDone()
// If this CAS succeeds, we know that the provided callable will never be invoked,
// so when oldFuture completes it is safe to allow the next submitted task to
// proceed.
|| (outputFuture.isCancelled() && runningState.compareAndSet(NOT_RUN, CANCELLED))) {
// Since the value of oldFuture can only ever be immediateFuture(null) or setFuture of // Since the value of oldFuture can only ever be immediateFuture(null) or setFuture of
// a future that eventually came from immediateFuture(null), this doesn't leak // a future that eventually came from immediateFuture(null), this doesn't leak
// throwables or completion values. // throwables or completion values.
newFuture.setFuture(oldFuture); newFuture.setFuture(oldFuture);
} else if (outputFuture.isCancelled() && taskExecutor.trySetCancelled()) {
// If this CAS succeeds, we know that the provided callable will never be invoked,
// so when oldFuture completes it is safe to allow the next submitted task to
// proceed. Doing this immediately here lets the next task run without waiting for
// the cancelled task's executor to run the noop AsyncCallable.
//
// ---
//
// If the CAS fails, the provided callable already started running (or it is about
// to). Our contract promises:
//
// 1. not to execute a new callable until the old one has returned
//
// If we were to cancel taskFuture, that would let the next task start while the old
// one is still running.
//
// Now, maybe we could tweak our implementation to not start the next task until the
// callable actually completes. (We could detect completion in our wrapper
// `AsyncCallable task`.) However, our contract also promises:
//
// 2. not to cancel any Future the user returned from an AsyncCallable
//
// We promise this because, once we cancel that Future, we would no longer be able to
// tell when any underlying work it is doing is done. Thus, we might start a new task
// while that underlying work is still running.
//
// So that is why we cancel only in the case of CAS success.
taskFuture.cancel(false);
} }
} }
}; };
@ -254,163 +164,4 @@ public final class ExecutionSequencer {
return outputFuture; return outputFuture;
} }
enum RunningState {
NOT_RUN,
CANCELLED,
STARTED,
}
/**
* This class helps avoid a StackOverflowError when large numbers of tasks are submitted with
* {@link MoreExecutors#directExecutor}. Normally, when the first future completes, all the other
* tasks would be called recursively. Here, we detect that the delegate executor is executing
* inline, and maintain a queue to dispatch tasks iteratively. There is one instance of this class
* per call to submit() or submitAsync(), and each instance supports only one call to execute().
*
* <p>This class would certainly be simpler and easier to reason about if it were built with
* ThreadLocal; however, ThreadLocal is not well optimized for the case where the ThreadLocal is
* non-static, and is initialized/removed frequently - this causes churn in the Thread specific
* hashmaps. Using a static ThreadLocal to avoid that overhead would mean that different
* ExecutionSequencer objects interfere with each other, which would be undesirable, in addition
* to increasing the memory footprint of every thread that interacted with it. In order to release
* entries in thread-specific maps when the ThreadLocal object itself is no longer referenced,
* ThreadLocal is usually implemented with a WeakReference, which can have negative performance
* properties; for example, calling WeakReference.get() on Android will block during an
* otherwise-concurrent GC cycle.
*/
@SuppressWarnings("ShouldNotSubclass") // Saving an allocation here is worth it
private static final class TaskNonReentrantExecutor extends AtomicReference<RunningState>
implements Executor, Runnable {
/**
* Used to update and read the latestTaskQueue field. Set to null once the runnable has been run
* or queued.
*/
ExecutionSequencer sequencer;
/**
* Executor the task was set to run on. Set to null when the task has been queued, run, or
* cancelled.
*/
Executor delegate;
/**
* Set before calling delegate.execute(); set to null once run, so that it can be GCed; this
* object may live on after, if submitAsync returns an incomplete future.
*/
Runnable task;
/** Thread that called execute(). Set in execute, cleared when delegate.execute() returns. */
Thread submitting;
private TaskNonReentrantExecutor(Executor delegate, ExecutionSequencer sequencer) {
super(NOT_RUN);
this.delegate = delegate;
this.sequencer = sequencer;
}
@Override
public void execute(Runnable task) {
// If this operation was successfully cancelled already, calling the runnable will be a noop.
// This also avoids a race where if outputFuture is cancelled, it will call taskFuture.cancel,
// which will call newFuture.setFuture(oldFuture), to allow the next task in the queue to run
// without waiting for the user's executor to run our submitted Runnable. However, this can
// interact poorly with the reentrancy-avoiding behavior of this executor - when the operation
// before the cancelled future completes, it will synchronously complete both the newFuture
// from the cancelled operation and its own. This can cause one runnable to queue two tasks,
// breaking the invariant this method relies on to iteratively run the next task after the
// previous one completes.
if (get() == RunningState.CANCELLED) {
delegate = null;
sequencer = null;
return;
}
submitting = Thread.currentThread();
try {
ThreadConfinedTaskQueue submittingTaskQueue = sequencer.latestTaskQueue;
if (submittingTaskQueue.thread == submitting) {
sequencer = null;
// Submit from inside a reentrant submit. We don't know if this one will be reentrant (and
// can't know without submitting something to the executor) so queue to run iteratively.
// Task must be null, since each execution on this executor can only produce one more
// execution.
checkState(submittingTaskQueue.nextTask == null);
submittingTaskQueue.nextTask = task;
submittingTaskQueue.nextExecutor = delegate;
delegate = null;
} else {
Executor localDelegate = delegate;
delegate = null;
this.task = task;
localDelegate.execute(this);
}
} finally {
// Important to null this out here - if we did *not* execute inline, we might still
// run() on the same thread that called execute() - such as in a thread pool, and think
// that it was happening inline. As a side benefit, avoids holding on to the Thread object
// longer than necessary.
submitting = null;
}
}
@SuppressWarnings("ShortCircuitBoolean")
@Override
public void run() {
Thread currentThread = Thread.currentThread();
if (currentThread != submitting) {
Runnable localTask = task;
task = null;
localTask.run();
return;
}
// Executor called reentrantly! Make sure that further calls don't overflow stack. Further
// reentrant calls will see that their current thread is the same as the one set in
// latestTaskQueue, and queue rather than calling execute() directly.
ThreadConfinedTaskQueue executingTaskQueue = new ThreadConfinedTaskQueue();
executingTaskQueue.thread = currentThread;
// Unconditionally set; there is no risk of throwing away a queued task from another thread,
// because in order for the current task to run on this executor the previous task must have
// already started execution. Because each task on a TaskNonReentrantExecutor can only produce
// one execute() call to another instance from the same ExecutionSequencer, we know by
// induction that the task that launched this one must not have added any other runnables to
// that thread's queue, and thus we cannot be replacing a TaskAndThread object that would
// otherwise have another task queued on to it. Note the exception to this, cancellation, is
// specially handled in execute() - execute() calls triggered by cancellation are no-ops, and
// thus don't count.
sequencer.latestTaskQueue = executingTaskQueue;
sequencer = null;
try {
Runnable localTask = task;
task = null;
localTask.run();
// Now check if our task attempted to reentrantly execute the next task.
Runnable queuedTask;
Executor queuedExecutor;
// Intentionally using non-short-circuit operator
while ((queuedTask = executingTaskQueue.nextTask) != null
& (queuedExecutor = executingTaskQueue.nextExecutor) != null) {
executingTaskQueue.nextTask = null;
executingTaskQueue.nextExecutor = null;
queuedExecutor.execute(queuedTask);
}
} finally {
// Null out the thread field, so that we don't leak a reference to Thread, and so that
// future `thread == currentThread()` calls from this thread don't incorrectly queue instead
// of executing. Don't null out the latestTaskQueue field, because the work done here
// may have scheduled more operations on another thread, and if those operations then
// trigger reentrant calls that thread will have updated the latestTaskQueue field, and
// we'd be interfering with their operation.
executingTaskQueue.thread = null;
}
}
private boolean trySetStarted() {
return compareAndSet(NOT_RUN, STARTED);
}
private boolean trySetCancelled() {
return compareAndSet(NOT_RUN, CANCELLED);
}
}
} }

View file

@ -49,7 +49,7 @@ import java.util.concurrent.TimeoutException;
* debugging, and cancellation. Examples of frameworks include: * debugging, and cancellation. Examples of frameworks include:
* *
* <ul> * <ul>
* <li><a href="https://dagger.dev/producers.html">Dagger Producers</a> * <li><a href="http://dagger.dev/producers.html">Dagger Producers</a>
* </ul> * </ul>
* *
* <h4>{@link java.util.concurrent.CompletableFuture} / {@link java.util.concurrent.CompletionStage} * <h4>{@link java.util.concurrent.CompletableFuture} / {@link java.util.concurrent.CompletionStage}

View file

@ -30,8 +30,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.CollectionFuture.ListFuture; import com.google.common.util.concurrent.CollectionFuture.ListFuture;
import com.google.common.util.concurrent.ImmediateFuture.ImmediateCancelledFuture; import com.google.common.util.concurrent.ImmediateFuture.ImmediateCancelledFuture;
import com.google.common.util.concurrent.ImmediateFuture.ImmediateFailedFuture; import com.google.common.util.concurrent.ImmediateFuture.ImmediateFailedFuture;
import com.google.common.util.concurrent.internal.InternalFutureFailureAccess;
import com.google.common.util.concurrent.internal.InternalFutures;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -46,6 +45,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Static utility methods pertaining to the {@link Future} interface. * Static utility methods pertaining to the {@link Future} interface.
* *
@ -60,7 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* monitoring, debugging, and cancellation. Examples of frameworks include: * monitoring, debugging, and cancellation. Examples of frameworks include:
* *
* <ul> * <ul>
* <li><a href="https://dagger.dev/producers.html">Dagger Producers</a> * <li><a href="http://dagger.dev/producers.html">Dagger Producers</a>
* </ul> * </ul>
* *
* <p>If you do chain your operations manually, you may want to use {@link FluentFuture}. * <p>If you do chain your operations manually, you may want to use {@link FluentFuture}.
@ -135,17 +135,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
return new ImmediateFuture<>(value); return new ImmediateFuture<>(value);
} }
/**
* Returns a successful {@code ListenableFuture<Void>}. This method is equivalent to {@code
* immediateFuture(null)} except that it is restricted to produce futures of type {@code Void}.
*
* @since 29.0
*/
@SuppressWarnings("unchecked")
public static ListenableFuture<Void> immediateVoidFuture() {
return (ListenableFuture<Void>) ImmediateFuture.NULL;
}
/** /**
* Returns a {@code ListenableFuture} which has an exception set immediately upon construction. * Returns a {@code ListenableFuture} which has an exception set immediately upon construction.
* *
@ -172,7 +161,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* Executes {@code callable} on the specified {@code executor}, returning a {@code Future}. * Executes {@code callable} on the specified {@code executor}, returning a {@code Future}.
* *
* @throws RejectedExecutionException if the task cannot be scheduled for execution * @throws RejectedExecutionException if the task cannot be scheduled for execution
* @since 28.2 * @since NEXT
*/ */
@Beta @Beta
public static <O> ListenableFuture<O> submit(Callable<O> callable, Executor executor) { public static <O> ListenableFuture<O> submit(Callable<O> callable, Executor executor) {
@ -186,7 +175,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* will complete after execution. * will complete after execution.
* *
* @throws RejectedExecutionException if the task cannot be scheduled for execution * @throws RejectedExecutionException if the task cannot be scheduled for execution
* @since 28.2 * @since NEXT
*/ */
@Beta @Beta
public static ListenableFuture<Void> submit(Runnable runnable, Executor executor) { public static ListenableFuture<Void> submit(Runnable runnable, Executor executor) {
@ -269,7 +258,9 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the warnings the {@link MoreExecutors#directExecutor} documentation. * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method.
* *
* @param input the primary input {@code Future} * @param input the primary input {@code Future}
* @param exceptionType the exception type that triggers use of {@code fallback}. The exception * @param exceptionType the exception type that triggers use of {@code fallback}. The exception
@ -334,7 +325,11 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the warnings the {@link MoreExecutors#directExecutor} documentation. * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method. (Specifically, {@code directExecutor} functions should avoid
* heavyweight operations inside {@code AsyncFunction.apply}. Any heavyweight operations should
* occur in other threads responsible for completing the returned {@code Future}.)
* *
* @param input the primary input {@code Future} * @param input the primary input {@code Future}
* @param exceptionType the exception type that triggers use of {@code fallback}. The exception * @param exceptionType the exception type that triggers use of {@code fallback}. The exception
@ -420,7 +415,11 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the warnings the {@link MoreExecutors#directExecutor} documentation. * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method. (Specifically, {@code directExecutor} functions should avoid
* heavyweight operations inside {@code AsyncFunction.apply}. Any heavyweight operations should
* occur in other threads responsible for completing the returned {@code Future}.)
* *
* <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the * <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the
* input future and that of the future returned by the chain function. That is, if the returned * input future and that of the future returned by the chain function. That is, if the returned
@ -456,7 +455,9 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the warnings the {@link MoreExecutors#directExecutor} documentation. * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method.
* *
* <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the * <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the
* input future. That is, if the returned {@code Future} is cancelled, it will attempt to cancel * input future. That is, if the returned {@code Future} is cancelled, it will attempt to cancel
@ -668,6 +669,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 20.0 * @since 20.0
*/ */
@Beta @Beta
// TODO(cpovirk): Consider removing, especially if we provide run(Runnable)
@GwtCompatible @GwtCompatible
public static final class FutureCombiner<V> { public static final class FutureCombiner<V> {
private final boolean allMustSucceed; private final boolean allMustSucceed;
@ -711,6 +713,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* *
* <p>Canceling this future will attempt to cancel all the component futures. * <p>Canceling this future will attempt to cancel all the component futures.
*/ */
// TODO(cpovirk): Remove this
public <C> ListenableFuture<C> call(Callable<C> combiner, Executor executor) { public <C> ListenableFuture<C> call(Callable<C> combiner, Executor executor) {
return new CombinedFuture<C>(futures, allMustSucceed, executor, combiner); return new CombinedFuture<C>(futures, allMustSucceed, executor, combiner);
} }
@ -996,18 +999,13 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
/** /**
* Registers separate success and failure callbacks to be run when the {@code Future}'s * Registers separate success and failure callbacks to be run when the {@code Future}'s
* computation is {@linkplain Future#isDone() complete} or, if the * computation is {@linkplain java.util.concurrent.Future#isDone() complete} or, if the
* computation is already complete, immediately. * computation is already complete, immediately.
* *
* <p>The callback is run on {@code executor}. There is no guaranteed ordering of execution of * <p>The callback is run on {@code executor}. There is no guaranteed ordering of execution of
* callbacks, but any callback added through this method is guaranteed to be called once the * callbacks, but any callback added through this method is guaranteed to be called once the
* computation is complete. * computation is complete.
* *
* <p>Exceptions thrown by a {@code callback} will be propagated up to the executor. Any exception
* thrown during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an
* exception thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught
* and logged.
*
* <p>Example: * <p>Example:
* *
* <pre>{@code * <pre>{@code
@ -1025,7 +1023,9 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the warnings the {@link MoreExecutors#directExecutor} documentation. * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* callbacks passed to this method.
* *
* <p>For a more general interface to attach a completion listener to a {@code Future}, see {@link * <p>For a more general interface to attach a completion listener to a {@code Future}, see {@link
* ListenableFuture#addListener addListener}. * ListenableFuture#addListener addListener}.
@ -1055,14 +1055,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
@Override @Override
public void run() { public void run() {
if (future instanceof InternalFutureFailureAccess) {
Throwable failure =
InternalFutures.tryInternalFastPathGetFailure((InternalFutureFailureAccess) future);
if (failure != null) {
callback.onFailure(failure);
return;
}
}
final V value; final V value;
try { try {
value = getDone(future); value = getDone(future);
@ -1101,6 +1093,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @throws IllegalStateException if the {@code Future} is not done * @throws IllegalStateException if the {@code Future} is not done
* @since 20.0 * @since 20.0
*/ */
// TODO(cpovirk): Consider calling getDone() in our own code. // TODO(cpovirk): Consider calling getDone() in our own code.
public static <V> V getDone(Future<V> future) throws ExecutionException { public static <V> V getDone(Future<V> future) throws ExecutionException {
/* /*
@ -1110,6 +1103,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* IllegalArgumentException here, in part to keep its recommendation simple: Static methods * IllegalArgumentException here, in part to keep its recommendation simple: Static methods
* should throw IllegalStateException only when they use static state. * should throw IllegalStateException only when they use static state.
* *
*
* Why do we deviate here? The answer: We want for fluentFuture.getDone() to throw the same * Why do we deviate here? The answer: We want for fluentFuture.getDone() to throw the same
* exception as Futures.getDone(fluentFuture). * exception as Futures.getDone(fluentFuture).
*/ */
@ -1160,6 +1154,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 19.0 (in 10.0 as {@code get}) * @since 19.0 (in 10.0 as {@code get})
*/ */
@Beta @Beta
@GwtIncompatible // reflection @GwtIncompatible // reflection
public static <V, X extends Exception> V getChecked(Future<V> future, Class<X> exceptionClass) public static <V, X extends Exception> V getChecked(Future<V> future, Class<X> exceptionClass)
throws X { throws X {
@ -1210,6 +1205,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 28.0 * @since 28.0
*/ */
@Beta @Beta
@GwtIncompatible // reflection @GwtIncompatible // reflection
public static <V, X extends Exception> V getChecked( public static <V, X extends Exception> V getChecked(
Future<V> future, Class<X> exceptionClass, Duration timeout) throws X { Future<V> future, Class<X> exceptionClass, Duration timeout) throws X {
@ -1260,6 +1256,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 19.0 (in 10.0 as {@code get} and with different parameter order) * @since 19.0 (in 10.0 as {@code get} and with different parameter order)
*/ */
@Beta @Beta
@GwtIncompatible // reflection @GwtIncompatible // reflection
@SuppressWarnings("GoodTime") // should accept a java.time.Duration @SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static <V, X extends Exception> V getChecked( public static <V, X extends Exception> V getChecked(
@ -1301,6 +1298,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @throws CancellationException if {@code get} throws a {@code CancellationException} * @throws CancellationException if {@code get} throws a {@code CancellationException}
* @since 10.0 * @since 10.0
*/ */
public static <V> V getUnchecked(Future<V> future) { public static <V> V getUnchecked(Future<V> future) {
checkNotNull(future); checkNotNull(future);
try { try {

View file

@ -40,7 +40,7 @@ import java.util.concurrent.RejectedExecutionException;
* frameworks include: * frameworks include:
* *
* <ul> * <ul>
* <li><a href="https://dagger.dev/producers.html">Dagger Producers</a> * <li><a href="http://dagger.dev/producers.html">Dagger Producers</a>
* </ul> * </ul>
* *
* <p>The main purpose of {@link #addListener addListener} is to support this chaining. You will * <p>The main purpose of {@link #addListener addListener} is to support this chaining. You will
@ -112,10 +112,20 @@ public interface ListenableFuture<V> extends Future<V> {
* thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught and * thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught and
* logged. * logged.
* *
* <p>Note: If your listener is lightweight -- and will not cause stack overflow by completing * <p>Note: For fast, lightweight listeners that would be safe to execute in any thread, consider
* more futures or adding more {@code directExecutor()} listeners inline -- consider {@link * {@link MoreExecutors#directExecutor}. Otherwise, avoid it. Heavyweight {@code directExecutor}
* MoreExecutors#directExecutor}. Otherwise, avoid it: See the warnings on the docs for {@code * listeners can cause problems, and these problems can be difficult to reproduce because they
* directExecutor}. * depend on timing. For example:
*
* <ul>
* <li>The listener may be executed by the caller of {@code addListener}. That caller may be a
* UI thread or other latency-sensitive thread. This can harm UI responsiveness.
* <li>The listener may be executed by the thread that completes this {@code Future}. That
* thread may be an internal system thread such as an RPC network thread. Blocking that
* thread may stall progress of the whole system. It may even cause a deadlock.
* <li>The listener may delay other listeners, even listeners that are not themselves {@code
* directExecutor} listeners.
* </ul>
* *
* <p>This is the most general listener interface. For common operations performed using * <p>This is the most general listener interface. For common operations performed using
* listeners, see {@link Futures}. For a simplified but general listener interface, see {@link * listeners, see {@link Futures}. For a simplified but general listener interface, see {@link

View file

@ -14,16 +14,11 @@
package com.google.common.util.concurrent; package com.google.common.util.concurrent;
import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/** /**
* A {@link FutureTask} that also implements the {@link ListenableFuture} interface. Unlike {@code * A {@link FutureTask} that also implements the {@link ListenableFuture} interface. Unlike {@code
@ -85,19 +80,6 @@ public class ListenableFutureTask<V> extends FutureTask<V> implements Listenable
executionList.add(listener, exec); executionList.add(listener, exec);
} }
@Override
public V get(long timeout, TimeUnit unit)
throws TimeoutException, InterruptedException, ExecutionException {
long timeoutNanos = unit.toNanos(timeout);
if (timeoutNanos <= OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD) {
return super.get(timeout, unit);
}
// Waiting 68 years should be enough for any program.
return super.get(
min(timeoutNanos, OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD), NANOSECONDS);
}
/** Internal implementation detail used to invoke the listeners. */ /** Internal implementation detail used to invoke the listeners. */
@Override @Override
protected void done() { protected void done() {

View file

@ -35,18 +35,21 @@ import java.util.concurrent.TimeUnit;
public interface ListeningExecutorService extends ExecutorService { public interface ListeningExecutorService extends ExecutorService {
/** /**
* @return a {@code ListenableFuture} representing pending completion of the task * @return a {@code ListenableFuture} representing pending completion of the task
* @throws RejectedExecutionException {@inheritDoc}
*/ */
@Override @Override
<T> ListenableFuture<T> submit(Callable<T> task); <T> ListenableFuture<T> submit(Callable<T> task);
/** /**
* @return a {@code ListenableFuture} representing pending completion of the task * @return a {@code ListenableFuture} representing pending completion of the task
* @throws RejectedExecutionException {@inheritDoc}
*/ */
@Override @Override
ListenableFuture<?> submit(Runnable task); ListenableFuture<?> submit(Runnable task);
/** /**
* @return a {@code ListenableFuture} representing pending completion of the task * @return a {@code ListenableFuture} representing pending completion of the task
* @throws RejectedExecutionException {@inheritDoc}
*/ */
@Override @Override
<T> ListenableFuture<T> submit(Runnable task, T result); <T> ListenableFuture<T> submit(Runnable task, T result);
@ -66,6 +69,8 @@ public interface ListeningExecutorService extends ExecutorService {
* @return A list of {@code ListenableFuture} instances representing the tasks, in the same * @return A list of {@code ListenableFuture} instances representing the tasks, in the same
* sequential order as produced by the iterator for the given task list, each of which has * sequential order as produced by the iterator for the given task list, each of which has
* completed. * completed.
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException if any task is null
*/ */
@Override @Override
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
@ -87,6 +92,8 @@ public interface ListeningExecutorService extends ExecutorService {
* sequential order as produced by the iterator for the given task list. If the operation did * sequential order as produced by the iterator for the given task list. If the operation did
* not time out, each task will have completed. If it did time out, some of these tasks will * not time out, each task will have completed. If it did time out, some of these tasks will
* not have completed. * not have completed.
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException if any task is null
*/ */
@Override @Override
<T> List<Future<T>> invokeAll( <T> List<Future<T>> invokeAll(

Some files were not shown because too many files have changed in this diff Show more