update to 4.4.2
This commit is contained in:
parent
70a8da3a4e
commit
79970ae966
221 changed files with 8310 additions and 10659 deletions
|
@ -1,7 +1,3 @@
|
||||||
sudo: false
|
|
||||||
language: java
|
language: java
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- openjdk11
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.m2
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ original [Google Guice Core Library](https://github.com/google/guice):
|
||||||
- code is conforming to JRE `compact1` profile
|
- code is conforming to JRE `compact1` profile
|
||||||
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
|
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
|
||||||
- removed all logging, uses of java.util.logger.* stuff and logger default binding
|
- removed all logging, uses of java.util.logger.* stuff and logger default binding
|
||||||
- removed all java.io.Serializable dependent stuff
|
- removed all java.io.Serializable dependent stuff. Note: everything which relies to be serializable was removed.
|
||||||
- reformatted source by IntelliJ and cleaned up source of @author and @since tags
|
- reformatted source by IntelliJ and cleaned up source of @author and @since tags
|
||||||
- target of compilation is Java 8, no support for Java 6/7
|
- target of compilation is Java 8, no support for Java 6/7
|
||||||
- introduced Gradle as build system
|
- introduced Gradle as build system
|
||||||
|
|
190
build.gradle
190
build.gradle
|
@ -1,50 +1,43 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "org.sonarqube" version "2.2"
|
id "org.sonarqube" version "2.8"
|
||||||
|
id "io.codearte.nexus-staging" version "0.21.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'maven'
|
apply plugin: 'maven'
|
||||||
apply plugin: 'signing'
|
|
||||||
apply plugin: 'findbugs'
|
|
||||||
apply plugin: 'pmd'
|
|
||||||
apply plugin: 'checkstyle'
|
|
||||||
apply plugin: "jacoco"
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
|
||||||
wagon
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "javax.inject:javax.inject:1"
|
compile "org.xbib:javax-inject:${project.property('javax-inject.version')}"
|
||||||
compile 'org.xbib:jsr-305:1.0.0'
|
compile "org.xbib:guava:${project.property('guava.version')}"
|
||||||
compile "com.google.guava:guava:21.0"
|
testCompile "org.junit.jupiter:junit-jupiter-api:${project.property('junit.version')}"
|
||||||
testCompile "junit:junit:4.12"
|
testCompile "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
|
||||||
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.7"
|
testCompile "org.junit.jupiter:junit-jupiter-engine:${project.property('junit.version')}"
|
||||||
testCompile "org.apache.logging.log4j:log4j-core:2.7"
|
testCompile "org.junit.vintage:junit-vintage-engine:${project.property('junit.version')}"
|
||||||
testCompile "javax.inject:javax.inject-tck:1"
|
testCompile "junit:junit:${project.property('junit4.version')}"
|
||||||
testCompile "com.google.guava:guava-testlib:21.0"
|
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
|
||||||
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
|
testCompile "com.google.guava:guava-testlib:28.1-jre"
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
compileJava {
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestJava {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
options.compilerArgs << "-Xlint:all" << "-profile" << "compact1"
|
options.compilerArgs << "-Xlint:all,-fallthrough"
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
exclude '*$*'
|
useJUnitPlatform()
|
||||||
exclude '**/ErrorHandlingTest*'
|
|
||||||
exclude '**/OSGiContainerTest*'
|
|
||||||
exclude '**/ScopesTest*'
|
|
||||||
exclude '**/TypeConversionTest*'
|
|
||||||
testLogging {
|
testLogging {
|
||||||
showStandardStreams = false
|
showStandardStreams = false
|
||||||
exceptionFormat = 'full'
|
exceptionFormat = 'full'
|
||||||
|
@ -55,18 +48,145 @@ task sourcesJar(type: Jar, dependsOn: classes) {
|
||||||
classifier 'sources'
|
classifier 'sources'
|
||||||
from sourceSets.main.allSource
|
from sourceSets.main.allSource
|
||||||
}
|
}
|
||||||
|
|
||||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||||
classifier 'javadoc'
|
classifier 'javadoc'
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
archives sourcesJar, javadocJar
|
archives sourcesJar, javadocJar
|
||||||
}
|
}
|
||||||
if (project.hasProperty('signing.keyId')) {
|
|
||||||
signing {
|
ext {
|
||||||
sign configurations.archives
|
user = 'xbib'
|
||||||
|
projectName = 'guice'
|
||||||
|
projectDescription = 'Guice for Java'
|
||||||
|
scmUrl = 'https://github.com/xbib/guice'
|
||||||
|
scmConnection = 'scm:git:git://github.com/xbib/guice.git'
|
||||||
|
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guice.git'
|
||||||
|
}
|
||||||
|
|
||||||
|
task sonatypeUpload(type: Upload) {
|
||||||
|
configuration = configurations.archives
|
||||||
|
uploadDescriptor = true
|
||||||
|
repositories {
|
||||||
|
if (project.hasProperty('ossrhUsername')) {
|
||||||
|
mavenDeployer {
|
||||||
|
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||||
|
repository(url: uri(ossrhReleaseUrl)) {
|
||||||
|
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||||
|
}
|
||||||
|
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
|
||||||
|
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||||
|
}
|
||||||
|
pom.project {
|
||||||
|
groupId project.group
|
||||||
|
artifactId project.name
|
||||||
|
version project.version
|
||||||
|
name project.name
|
||||||
|
description projectDescription
|
||||||
|
packaging 'jar'
|
||||||
|
inceptionYear '2016'
|
||||||
|
url scmUrl
|
||||||
|
organization {
|
||||||
|
name 'xbib'
|
||||||
|
url 'http://xbib.org'
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id user
|
||||||
|
name 'Jörg Prante'
|
||||||
|
email 'joergprante@gmail.com'
|
||||||
|
url 'https://github.com/jprante'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
url scmUrl
|
||||||
|
connection scmConnection
|
||||||
|
developerConnection scmDeveloperConnection
|
||||||
|
}
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name 'The Apache License, Version 2.0'
|
||||||
|
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "${rootProject.projectDir}/gradle/ext.gradle"
|
|
||||||
apply from: "${rootProject.projectDir}/gradle/publish.gradle"
|
nexusStaging {
|
||||||
apply from: "${rootProject.projectDir}/gradle/sonarqube.gradle"
|
packageGroup = "org.xbib"
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
spotbugs {
|
||||||
|
effort = "max"
|
||||||
|
reportLevel = "low"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(com.github.spotbugs.SpotBugsTask) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = false
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Pmd) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = true
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Checkstyle) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = true
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pmd {
|
||||||
|
toolVersion = '6.11.0'
|
||||||
|
ruleSets = ['category/java/bestpractices.xml']
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
|
||||||
|
ignoreFailures = true
|
||||||
|
showViolations = false
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
compileJava {
|
||||||
|
inputs.property("moduleName", 'org.xbib.guice')
|
||||||
|
doFirst {
|
||||||
|
options.compilerArgs = ['--module-path', classpath.asPath]
|
||||||
|
classpath = files()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
javadoc {
|
||||||
|
inputs.property("moduleName", 'org.xbib.guice')
|
||||||
|
doFirst {
|
||||||
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
|
options.addStringOption('-module-path', classpath.asPath)
|
||||||
|
classpath = files()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,11 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = guice
|
name = guice
|
||||||
version = 4.0.4
|
version = 4.4.2
|
||||||
|
|
||||||
|
javax-inject.version = 1
|
||||||
|
guava.version = 28.1
|
||||||
|
|
||||||
|
# test
|
||||||
|
junit.version = 5.5.2
|
||||||
|
junit4.version = 4.12
|
||||||
|
log4j.version = 2.12.1
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
ext {
|
|
||||||
user = 'xbib'
|
|
||||||
projectName = 'guice'
|
|
||||||
projectDescription = 'Guice for Java'
|
|
||||||
scmUrl = 'https://github.com/xbib/guice'
|
|
||||||
scmConnection = 'scm:git:git://github.com/xbib/guice.git'
|
|
||||||
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guice.git'
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
|
|
||||||
task xbibUpload(type: Upload) {
|
|
||||||
configuration = configurations.archives
|
|
||||||
uploadDescriptor = true
|
|
||||||
repositories {
|
|
||||||
if (project.hasProperty('xbibUsername')) {
|
|
||||||
mavenDeployer {
|
|
||||||
configuration = configurations.wagon
|
|
||||||
repository(url: uri('scpexe://xbib.org/repository')) {
|
|
||||||
authentication(userName: xbibUsername, privateKey: xbibPrivateKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task sonatypeUpload(type: Upload) {
|
|
||||||
configuration = configurations.archives
|
|
||||||
uploadDescriptor = true
|
|
||||||
repositories {
|
|
||||||
if (project.hasProperty('ossrhUsername')) {
|
|
||||||
mavenDeployer {
|
|
||||||
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
|
||||||
repository(url: uri(ossrhReleaseUrl)) {
|
|
||||||
authentication(userName: ossrhUsername, password: ossrhPassword)
|
|
||||||
}
|
|
||||||
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
|
|
||||||
authentication(userName: ossrhUsername, password: ossrhPassword)
|
|
||||||
}
|
|
||||||
pom.project {
|
|
||||||
groupId project.group
|
|
||||||
artifactId project.name
|
|
||||||
version project.version
|
|
||||||
name project.name
|
|
||||||
description projectDescription
|
|
||||||
packaging 'jar'
|
|
||||||
inceptionYear '2016'
|
|
||||||
url scmUrl
|
|
||||||
organization {
|
|
||||||
name 'xbib'
|
|
||||||
url 'http://xbib.org'
|
|
||||||
}
|
|
||||||
developers {
|
|
||||||
developer {
|
|
||||||
id user
|
|
||||||
name 'Jörg Prante'
|
|
||||||
email 'joergprante@gmail.com'
|
|
||||||
url 'https://github.com/jprante'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scm {
|
|
||||||
url scmUrl
|
|
||||||
connection scmConnection
|
|
||||||
developerConnection scmDeveloperConnection
|
|
||||||
}
|
|
||||||
licenses {
|
|
||||||
license {
|
|
||||||
name 'The Apache License, Version 2.0'
|
|
||||||
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
tasks.withType(FindBugs) {
|
|
||||||
ignoreFailures = true
|
|
||||||
reports {
|
|
||||||
xml.enabled = true
|
|
||||||
html.enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks.withType(Pmd) {
|
|
||||||
ignoreFailures = true
|
|
||||||
reports {
|
|
||||||
xml.enabled = true
|
|
||||||
html.enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks.withType(Checkstyle) {
|
|
||||||
ignoreFailures = true
|
|
||||||
reports {
|
|
||||||
xml.enabled = true
|
|
||||||
html.enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jacocoTestReport {
|
|
||||||
reports {
|
|
||||||
xml.enabled true
|
|
||||||
csv.enabled false
|
|
||||||
xml.destination "${buildDir}/reports/jacoco-xml"
|
|
||||||
html.destination "${buildDir}/reports/jacoco-html"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
property "sonar.java.coveragePlugin", "jacoco"
|
|
||||||
property "sonar.junit.reportsPath", "build/test-results/test/"
|
|
||||||
}
|
|
||||||
}
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
#Fri Feb 03 12:38:43 CET 2017
|
#Wed Nov 20 21:27:17 CET 2019
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
28
gradlew
vendored
28
gradlew
vendored
|
@ -1,5 +1,21 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
|
@ -28,16 +44,16 @@ APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS=""
|
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 "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
die ( ) {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
|
@ -109,8 +125,8 @@ if $darwin; then
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; 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"`
|
||||||
|
@ -155,7 +171,7 @@ if $cygwin ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Escape application args
|
||||||
save ( ) {
|
save () {
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
|
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
|
@ -1,3 +1,19 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
|
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
|
@ -36,6 +36,7 @@ public abstract class AbstractModule implements Module {
|
||||||
|
|
||||||
Binder binder;
|
Binder binder;
|
||||||
|
|
||||||
|
@Override
|
||||||
public final synchronized void configure(Binder builder) {
|
public final synchronized void configure(Binder builder) {
|
||||||
checkState(this.binder == null, "Re-entry is not allowed.");
|
checkState(this.binder == null, "Re-entry is not allowed.");
|
||||||
|
|
||||||
|
@ -63,8 +64,7 @@ public abstract class AbstractModule implements Module {
|
||||||
/**
|
/**
|
||||||
* @see Binder#bindScope(Class, Scope)
|
* @see Binder#bindScope(Class, Scope)
|
||||||
*/
|
*/
|
||||||
protected void bindScope(Class<? extends Annotation> scopeAnnotation,
|
protected void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
|
||||||
Scope scope) {
|
|
||||||
binder().bindScope(scopeAnnotation, scope);
|
binder().bindScope(scopeAnnotation, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -367,7 +367,7 @@ public interface Binder {
|
||||||
* their clients.
|
* their clients.
|
||||||
* @return a binder that shares its configuration with this binder.
|
* @return a binder that shares its configuration with this binder.
|
||||||
*/
|
*/
|
||||||
Binder skipSources(Class... classesToSkip);
|
Binder skipSources(Class<?>... classesToSkip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new private child environment for bindings and other configuration. The returned
|
* Creates a new private child environment for bindings and other configuration. The returned
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.internal.Errors;
|
import com.google.inject.internal.Messages;
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -24,7 +24,7 @@ public final class ConfigurationException extends RuntimeException {
|
||||||
*/
|
*/
|
||||||
public ConfigurationException(Iterable<Message> messages) {
|
public ConfigurationException(Iterable<Message> messages) {
|
||||||
this.messages = ImmutableSet.copyOf(messages);
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
initCause(Errors.getOnlyCause(this.messages));
|
initCause(Messages.getOnlyCause(this.messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +59,6 @@ public final class ConfigurationException extends RuntimeException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return Errors.format("Guice configuration errors", messages);
|
return Messages.formatMessages("Guice configuration errors", messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.internal.Errors;
|
import com.google.inject.internal.Messages;
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -12,9 +12,9 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||||
* Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered
|
* Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered
|
||||||
* errors. Clients should catch this exception, log it, and stop execution.
|
* errors. Clients should catch this exception, log it, and stop execution.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
public class CreationException extends RuntimeException {
|
public class CreationException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 0;
|
|
||||||
private final ImmutableSet<Message> messages;
|
private final ImmutableSet<Message> messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,7 @@ public class CreationException extends RuntimeException {
|
||||||
public CreationException(Collection<Message> messages) {
|
public CreationException(Collection<Message> messages) {
|
||||||
this.messages = ImmutableSet.copyOf(messages);
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
checkArgument(!this.messages.isEmpty());
|
checkArgument(!this.messages.isEmpty());
|
||||||
initCause(Errors.getOnlyCause(this.messages));
|
initCause(Messages.getOnlyCause(this.messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,6 +35,6 @@ public class CreationException extends RuntimeException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return Errors.format("Unable to create injector, see the following errors", messages);
|
return Messages.formatMessages("Unable to create injector, see the following errors", messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,11 +76,7 @@ public final class Guice {
|
||||||
* @throws CreationException if one or more errors occur during injector
|
* @throws CreationException if one or more errors occur during injector
|
||||||
* construction
|
* construction
|
||||||
*/
|
*/
|
||||||
public static Injector createInjector(Stage stage,
|
public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) {
|
||||||
Iterable<? extends Module> modules) {
|
return new InternalInjectorCreator().stage(stage).addModules(modules).build();
|
||||||
return new InternalInjectorCreator()
|
|
||||||
.stage(stage)
|
|
||||||
.addModules(modules)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
@ -225,4 +227,37 @@ public interface Injector {
|
||||||
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
*/
|
*/
|
||||||
Set<TypeConverterBinding> getTypeConverterBindings();
|
Set<TypeConverterBinding> getTypeConverterBindings();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the elements that make up this injector. Note that not all kinds of elements are
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* <p>The returned list does not include elements inherited from a {@link #getParent() parent
|
||||||
|
* injector}, should one exist.
|
||||||
|
*
|
||||||
|
* <p>The returned list is immutable; it contains only the elements that were present when {@link
|
||||||
|
* #getElements} was invoked. Subsequent calls may return a list with additional elements.
|
||||||
|
*
|
||||||
|
* <p>The returned list does not include data inherited from a {@link #getParent() parent
|
||||||
|
* injector}, should one exist.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
List<Element> getElements();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the injection points created for calls to {@link #getMembersInjector} (either directly
|
||||||
|
* or indirectly, e.g. through {@link #injectMembers}.
|
||||||
|
*
|
||||||
|
* <p>This excludes any injection points from elements (which are accessible from each element via
|
||||||
|
* the SPI), unless {@link #getMembersInjector} or {@link #injectMembers} were also called for the
|
||||||
|
* same key.
|
||||||
|
*
|
||||||
|
* <p>The returned multimap does not include data inherited from a {@link #getParent() parent
|
||||||
|
* injector}, should one exist.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,13 @@ public class Key<T> {
|
||||||
private final AnnotationStrategy annotationStrategy;
|
private final AnnotationStrategy annotationStrategy;
|
||||||
|
|
||||||
private final TypeLiteral<T> typeLiteral;
|
private final TypeLiteral<T> typeLiteral;
|
||||||
|
|
||||||
private final int hashCode;
|
private final int hashCode;
|
||||||
private final Supplier<String> toStringSupplier;
|
|
||||||
|
// This field is updated using the 'Data-Race-Ful' lazy intialization pattern
|
||||||
|
// See http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html for a detailed
|
||||||
|
// explanation.
|
||||||
|
private String toString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new key. Derives the type from this class's type parameter.
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
|
@ -60,7 +65,6 @@ public class Key<T> {
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,7 +86,6 @@ public class Key<T> {
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,7 +105,6 @@ public class Key<T> {
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,7 +115,6 @@ public class Key<T> {
|
||||||
this.annotationStrategy = annotationStrategy;
|
this.annotationStrategy = annotationStrategy;
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,14 +124,12 @@ public class Key<T> {
|
||||||
this.annotationStrategy = annotationStrategy;
|
this.annotationStrategy = annotationStrategy;
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a key for an injection type and an annotation strategy.
|
* Gets a key for an injection type and an annotation strategy.
|
||||||
*/
|
*/
|
||||||
static <T> Key<T> get(Class<T> type,
|
static <T> Key<T> get(Class<T> type, AnnotationStrategy annotationStrategy) {
|
||||||
AnnotationStrategy annotationStrategy) {
|
|
||||||
return new Key<T>(type, annotationStrategy);
|
return new Key<T>(type, annotationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +143,7 @@ public class Key<T> {
|
||||||
/**
|
/**
|
||||||
* Gets a key for an injection type and an annotation type.
|
* Gets a key for an injection type and an annotation type.
|
||||||
*/
|
*/
|
||||||
public static <T> Key<T> get(Class<T> type,
|
public static <T> Key<T> get(Class<T> type, Class<? extends Annotation> annotationType) {
|
||||||
Class<? extends Annotation> annotationType) {
|
|
||||||
return new Key<T>(type, strategyFor(annotationType));
|
return new Key<T>(type, strategyFor(annotationType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,8 +164,7 @@ public class Key<T> {
|
||||||
/**
|
/**
|
||||||
* Gets a key for an injection type and an annotation type.
|
* Gets a key for an injection type and an annotation type.
|
||||||
*/
|
*/
|
||||||
public static Key<?> get(Type type,
|
public static Key<?> get(Type type, Class<? extends Annotation> annotationType) {
|
||||||
Class<? extends Annotation> annotationType) {
|
|
||||||
return new Key<Object>(type, strategyFor(annotationType));
|
return new Key<Object>(type, strategyFor(annotationType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,16 +185,14 @@ public class Key<T> {
|
||||||
/**
|
/**
|
||||||
* Gets a key for an injection type and an annotation type.
|
* Gets a key for an injection type and an annotation type.
|
||||||
*/
|
*/
|
||||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
|
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Class<? extends Annotation> annotationType) {
|
||||||
Class<? extends Annotation> annotationType) {
|
|
||||||
return new Key<T>(typeLiteral, strategyFor(annotationType));
|
return new Key<T>(typeLiteral, strategyFor(annotationType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a key for an injection type and an annotation.
|
* Gets a key for an injection type and an annotation.
|
||||||
*/
|
*/
|
||||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
|
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Annotation annotation) {
|
||||||
Annotation annotation) {
|
|
||||||
return new Key<T>(typeLiteral, strategyFor(annotation));
|
return new Key<T>(typeLiteral, strategyFor(annotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,20 +248,6 @@ public class Key<T> {
|
||||||
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
|
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a {@link Supplier} which memoizes the value for lazy initialization.
|
|
||||||
*/
|
|
||||||
private Supplier<String> createToStringSupplier() {
|
|
||||||
// The performance hit on access is acceptable since the intended use is for non-performance-
|
|
||||||
// critical applications.
|
|
||||||
return Suppliers.memoize(new Supplier<String>() {
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the key type.
|
* Gets the key type.
|
||||||
*/
|
*/
|
||||||
|
@ -333,7 +314,14 @@ public class Key<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String toString() {
|
public final String toString() {
|
||||||
return toStringSupplier.get();
|
// Note: to not introduce dangerous data races the field should only be read once in this
|
||||||
|
// method.
|
||||||
|
String local = toString;
|
||||||
|
if (local == null) {
|
||||||
|
local = "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
|
||||||
|
toString = local;
|
||||||
|
}
|
||||||
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -375,21 +363,25 @@ public class Key<T> {
|
||||||
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
|
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum NullAnnotationStrategy implements AnnotationStrategy {
|
enum NullAnnotationStrategy implements AnnotationStrategy {
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasAttributes() {
|
public boolean hasAttributes() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AnnotationStrategy withoutAttributes() {
|
public AnnotationStrategy withoutAttributes() {
|
||||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Annotation getAnnotation() {
|
public Annotation getAnnotation() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -401,6 +393,7 @@ public class Key<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AnnotationStrategy {
|
interface AnnotationStrategy {
|
||||||
|
|
||||||
Annotation getAnnotation();
|
Annotation getAnnotation();
|
||||||
|
|
||||||
Class<? extends Annotation> getAnnotationType();
|
Class<? extends Annotation> getAnnotationType();
|
||||||
|
@ -419,18 +412,22 @@ public class Key<T> {
|
||||||
this.annotation = checkNotNull(annotation, "annotation");
|
this.annotation = checkNotNull(annotation, "annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasAttributes() {
|
public boolean hasAttributes() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AnnotationStrategy withoutAttributes() {
|
public AnnotationStrategy withoutAttributes() {
|
||||||
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Annotation getAnnotation() {
|
public Annotation getAnnotation() {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
return annotation.annotationType();
|
return annotation.annotationType();
|
||||||
}
|
}
|
||||||
|
@ -469,18 +466,22 @@ public class Key<T> {
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasAttributes() {
|
public boolean hasAttributes() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AnnotationStrategy withoutAttributes() {
|
public AnnotationStrategy withoutAttributes() {
|
||||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Annotation getAnnotation() {
|
public Annotation getAnnotation() {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
return annotationType;
|
return annotationType;
|
||||||
}
|
}
|
||||||
|
|
22
src/main/java/com/google/inject/OutOfScopeException.java
Normal file
22
src/main/java/com/google/inject/OutOfScopeException.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown from {@link Provider#get} when an attempt is made to access a scoped object while the
|
||||||
|
* scope in question is not currently active.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public final class OutOfScopeException extends RuntimeException {
|
||||||
|
|
||||||
|
public OutOfScopeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutOfScopeException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutOfScopeException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,5 +31,5 @@ public interface PrivateBinder extends Binder {
|
||||||
|
|
||||||
PrivateBinder withSource(Object source);
|
PrivateBinder withSource(Object source);
|
||||||
|
|
||||||
PrivateBinder skipSources(Class... classesToSkip);
|
PrivateBinder skipSources(Class<?>... classesToSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
|
@ -32,5 +32,6 @@ public interface Provider<T> extends javax.inject.Provider<T> {
|
||||||
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
|
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
|
||||||
* and throwables to describe why provision failed.
|
* and throwables to describe why provision failed.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
T get();
|
T get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.internal.Errors;
|
import com.google.inject.internal.Messages;
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -23,7 +23,7 @@ public final class ProvisionException extends RuntimeException {
|
||||||
public ProvisionException(Iterable<Message> messages) {
|
public ProvisionException(Iterable<Message> messages) {
|
||||||
this.messages = ImmutableSet.copyOf(messages);
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
checkArgument(!this.messages.isEmpty());
|
checkArgument(!this.messages.isEmpty());
|
||||||
initCause(Errors.getOnlyCause(this.messages));
|
initCause(Messages.getOnlyCause(this.messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProvisionException(String message, Throwable cause) {
|
public ProvisionException(String message, Throwable cause) {
|
||||||
|
@ -44,6 +44,6 @@ public final class ProvisionException extends RuntimeException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return Errors.format("Unable to provision, see the following errors", messages);
|
return Messages.formatMessages("Unable to provision, see the following errors", messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class Scopes {
|
||||||
* this to "no scope" in your binding.
|
* this to "no scope" in your binding.
|
||||||
*/
|
*/
|
||||||
public static final Scope NO_SCOPE = new Scope() {
|
public static final Scope NO_SCOPE = new Scope() {
|
||||||
|
@Override
|
||||||
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
||||||
return unscoped;
|
return unscoped;
|
||||||
}
|
}
|
||||||
|
@ -37,21 +38,26 @@ public class Scopes {
|
||||||
return "Scopes.NO_SCOPE";
|
return "Scopes.NO_SCOPE";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
||||||
= new BindingScopingVisitor<Boolean>() {
|
= new BindingScopingVisitor<>() {
|
||||||
|
@Override
|
||||||
public Boolean visitNoScoping() {
|
public Boolean visitNoScoping() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
||||||
return scopeAnnotation == Singleton.class
|
return scopeAnnotation == Singleton.class
|
||||||
|| scopeAnnotation == javax.inject.Singleton.class;
|
|| scopeAnnotation == javax.inject.Singleton.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScope(Scope scope) {
|
public Boolean visitScope(Scope scope) {
|
||||||
return scope == Scopes.SINGLETON;
|
return scope == Scopes.SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitEagerSingleton() {
|
public Boolean visitEagerSingleton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -107,18 +113,22 @@ public class Scopes {
|
||||||
final Class<? extends Annotation> scopeAnnotation) {
|
final Class<? extends Annotation> scopeAnnotation) {
|
||||||
do {
|
do {
|
||||||
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
||||||
|
@Override
|
||||||
public Boolean visitNoScoping() {
|
public Boolean visitNoScoping() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
||||||
return visitedAnnotation == scopeAnnotation;
|
return visitedAnnotation == scopeAnnotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScope(Scope visitedScope) {
|
public Boolean visitScope(Scope visitedScope) {
|
||||||
return visitedScope == scope;
|
return visitedScope == scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitEagerSingleton() {
|
public Boolean visitEagerSingleton() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class TypeLiteral<T> {
|
||||||
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
||||||
while (true) {
|
while (true) {
|
||||||
if (toResolve instanceof TypeVariable) {
|
if (toResolve instanceof TypeVariable) {
|
||||||
TypeVariable original = (TypeVariable) toResolve;
|
TypeVariable<?> original = (TypeVariable) toResolve;
|
||||||
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
||||||
if (toResolve == original) {
|
if (toResolve == original) {
|
||||||
return toResolve;
|
return toResolve;
|
||||||
|
|
|
@ -13,6 +13,7 @@ import com.google.inject.Provider;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.internal.Errors;
|
import com.google.inject.internal.Errors;
|
||||||
import com.google.inject.internal.ErrorsException;
|
import com.google.inject.internal.ErrorsException;
|
||||||
|
import com.google.inject.internal.Messages;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.HasDependencies;
|
import com.google.inject.spi.HasDependencies;
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
|
@ -256,7 +257,7 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigurationException newConfigurationException(String format, Object... args) {
|
private static ConfigurationException newConfigurationException(String format, Object... args) {
|
||||||
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
|
return new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -355,9 +356,9 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
|
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
|
||||||
Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
|
Class<F> factoryRawType = (Class<F>) factoryType.getRawType();
|
||||||
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
|
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
|
||||||
new Class[]{factoryRawType}, invocationHandler));
|
new Class<?>[]{factoryRawType}, invocationHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -96,7 +96,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "@" + Assisted.class.getName() + "(value=)";
|
return "@" + Assisted.class.getName() + "(value=\"\")";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
@ -555,7 +555,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
+ "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
|
+ "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
|
||||||
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) "
|
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) "
|
||||||
+ "The exact offending deps are: {2}",
|
+ "The exact offending deps are: {2}",
|
||||||
new Object[]{factoryType, implementation, badDeps});
|
new Object[] { factoryType, implementation, badDeps } );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -51,20 +51,17 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
ScopedBindingBuilder toProvider(
|
ScopedBindingBuilder toProvider(Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||||
Class<? extends javax.inject.Provider<? extends T>> providerType);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
ScopedBindingBuilder toProvider(
|
ScopedBindingBuilder toProvider(TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
ScopedBindingBuilder toProvider(
|
ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
@ -74,6 +71,5 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
<S extends T> ScopedBindingBuilder toConstructor(
|
<S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor, TypeLiteral<? extends S> type);
|
||||||
Constructor<S> constructor, TypeLiteral<? extends S> type);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
public abstract class AbstractBindingBuilder<T> {
|
public abstract class AbstractBindingBuilder<T> {
|
||||||
|
|
||||||
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
||||||
public static final String SINGLE_INSTANCE_AND_SCOPE
|
public static final String SINGLE_INSTANCE_AND_SCOPE =
|
||||||
= "Setting the scope is not permitted when binding to a single instance.";
|
"Setting the scope is not permitted when binding to a single instance.";
|
||||||
public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
|
public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
|
||||||
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. "
|
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. " +
|
||||||
+ "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
|
"Use toProvider(Providers.of(null)) if this is your intended behaviour.";
|
||||||
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
|
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
|
||||||
public static final String ANNOTATION_ALREADY_SPECIFIED
|
public static final String ANNOTATION_ALREADY_SPECIFIED =
|
||||||
= "More than one annotation is specified for this binding.";
|
"More than one annotation is specified for this binding.";
|
||||||
|
|
||||||
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
||||||
protected final Binder binder;
|
protected final Binder binder;
|
||||||
|
@ -37,7 +37,7 @@ public abstract class AbstractBindingBuilder<T> {
|
||||||
this.binder = binder;
|
this.binder = binder;
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.position = elements.size();
|
this.position = elements.size();
|
||||||
this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED);
|
this.binding = new UntargettedBindingImpl<>(source, key, Scoping.UNSCOPED);
|
||||||
elements.add(position, this.binding);
|
elements.add(position, this.binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +57,7 @@ public abstract class AbstractBindingBuilder<T> {
|
||||||
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
||||||
checkNotNull(annotationType, "annotationType");
|
checkNotNull(annotationType, "annotationType");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
return setBinding(binding.withKey(
|
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||||
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,8 +66,7 @@ public abstract class AbstractBindingBuilder<T> {
|
||||||
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
||||||
checkNotNull(annotation, "annotation");
|
checkNotNull(annotation, "annotation");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
return setBinding(binding.withKey(
|
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||||
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void in(final Class<? extends Annotation> scopeAnnotation) {
|
public void in(final Class<? extends Annotation> scopeAnnotation) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
// It's unfortunate that we have to maintain a blacklist of specific
|
// It's unfortunate that we have to maintain a blacklist of specific
|
||||||
// classes, but we can't easily block the whole package because of
|
// classes, but we can't easily block the whole package because of
|
||||||
// all our unit tests.
|
// all our unit tests.
|
||||||
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(
|
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.of(
|
||||||
AbstractModule.class,
|
AbstractModule.class,
|
||||||
Binder.class,
|
Binder.class,
|
||||||
Binding.class,
|
Binding.class,
|
||||||
|
@ -93,7 +93,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
*/
|
*/
|
||||||
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
|
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
|
||||||
if (original instanceof ExposedBindingImpl) {
|
if (original instanceof ExposedBindingImpl) {
|
||||||
ExposedBindingImpl exposed = (ExposedBindingImpl) original;
|
ExposedBindingImpl<?> exposed = (ExposedBindingImpl) original;
|
||||||
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
||||||
return (exposedFrom == binding.getInjector());
|
return (exposedFrom == binding.getInjector());
|
||||||
} else {
|
} else {
|
||||||
|
@ -131,16 +131,28 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void scheduleInitialization(final BindingImpl<?> binding) {
|
/**
|
||||||
bindingData.addUninitializedBinding(new Runnable() {
|
* Schedule initialization of this binding to occur immediately after all bindings have been
|
||||||
public void run() {
|
* initialially processed.
|
||||||
|
*/
|
||||||
|
protected void scheduleInitialization(BindingImpl<?> binding) {
|
||||||
|
bindingData.addUninitializedBinding(() -> initializeBinding(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule initialization for this binding to occur after all other static initialization of
|
||||||
|
* bindings.
|
||||||
|
*/
|
||||||
|
protected void scheduleDelayedInitialization(BindingImpl<?> binding) {
|
||||||
|
bindingData.addDelayedUninitializedBinding(() -> initializeBinding(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeBinding(BindingImpl<?> binding) {
|
||||||
try {
|
try {
|
||||||
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||||
} catch (ErrorsException e) {
|
} catch (ErrorsException e) {
|
||||||
errors.merge(e.getErrors());
|
errors.merge(e.getErrors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Joiner.MapJoiner;
|
import com.google.common.base.Joiner.MapJoiner;
|
||||||
|
@ -43,14 +45,17 @@ public class Annotations {
|
||||||
return s.substring(1, s.length() - 1); // cut off brackets
|
return s.substring(1, s.length() - 1); // cut off brackets
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
|
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
|
||||||
CacheBuilder.newBuilder().weakKeys().build(
|
CacheBuilder.newBuilder()
|
||||||
new CacheLoader<Class<? extends Annotation>, Annotation>() {
|
.weakKeys()
|
||||||
|
.build(new CacheLoader<>() {
|
||||||
@Override
|
@Override
|
||||||
public Annotation load(Class<? extends Annotation> input) {
|
public Annotation load(Class<? extends Annotation> input) {
|
||||||
return generateAnnotationImpl(input);
|
return generateAnnotationImpl(input);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
private static final AnnotationChecker scopeChecker = new AnnotationChecker(
|
private static final AnnotationChecker scopeChecker = new AnnotationChecker(
|
||||||
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
|
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
|
||||||
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
|
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
|
||||||
|
@ -90,22 +95,20 @@ public class Annotations {
|
||||||
return annotationType.cast(Proxy.newProxyInstance(
|
return annotationType.cast(Proxy.newProxyInstance(
|
||||||
annotationType.getClassLoader(),
|
annotationType.getClassLoader(),
|
||||||
new Class<?>[]{annotationType},
|
new Class<?>[]{annotationType},
|
||||||
new InvocationHandler() {
|
(proxy, method, args) -> {
|
||||||
@Override
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
|
|
||||||
String name = method.getName();
|
String name = method.getName();
|
||||||
if (name.equals("annotationType")) {
|
switch (name) {
|
||||||
|
case "annotationType":
|
||||||
return annotationType;
|
return annotationType;
|
||||||
} else if (name.equals("toString")) {
|
case "toString":
|
||||||
return annotationToString(annotationType, members);
|
return annotationToString(annotationType, members);
|
||||||
} else if (name.equals("hashCode")) {
|
case "hashCode":
|
||||||
return annotationHashCode(annotationType, members);
|
return annotationHashCode(annotationType, members);
|
||||||
} else if (name.equals("equals")) {
|
case "equals":
|
||||||
return annotationEquals(annotationType, members, args[0]);
|
return annotationEquals(annotationType, members, args[0]);
|
||||||
} else {
|
default:
|
||||||
return members.get(name);
|
return members.get(name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +210,30 @@ public class Annotations {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final boolean QUOTE_MEMBER_VALUES = determineWhetherToQuote();
|
||||||
|
|
||||||
|
public static String memberValueString(String value) {
|
||||||
|
return QUOTE_MEMBER_VALUES ? "\"" + value + "\"" : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
private @interface TestAnnotation {
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestAnnotation("determineWhetherToQuote")
|
||||||
|
private static boolean determineWhetherToQuote() {
|
||||||
|
try {
|
||||||
|
String annotation = Annotations.class
|
||||||
|
.getDeclaredMethod("determineWhetherToQuote")
|
||||||
|
.getAnnotation(TestAnnotation.class)
|
||||||
|
.toString();
|
||||||
|
return annotation.contains("\"determineWhetherToQuote\"");
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
|
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
return scopeChecker.hasAnnotations(annotationType);
|
return scopeChecker.hasAnnotations(annotationType);
|
||||||
}
|
}
|
||||||
|
@ -243,8 +270,7 @@ public class Annotations {
|
||||||
/**
|
/**
|
||||||
* Returns the binding annotation on {@code member}, or null if there isn't one.
|
* Returns the binding annotation on {@code member}, or null if there isn't one.
|
||||||
*/
|
*/
|
||||||
public static Annotation findBindingAnnotation(
|
public static Annotation findBindingAnnotation(Errors errors, Member member, Annotation[] annotations) {
|
||||||
Errors errors, Member member, Annotation[] annotations) {
|
|
||||||
Annotation found = null;
|
Annotation found = null;
|
||||||
|
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
|
@ -293,6 +319,24 @@ public class Annotations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name the binding should use. This is based on the annotation. If the annotation has
|
||||||
|
* an instance and is not a marker annotation, we ask the annotation for its toString. If it was a
|
||||||
|
* marker annotation or just an annotation type, we use the annotation's name. Otherwise, the name
|
||||||
|
* is the empty string.
|
||||||
|
*/
|
||||||
|
public static String nameOf(Key<?> key) {
|
||||||
|
Annotation annotation = key.getAnnotation();
|
||||||
|
Class<? extends Annotation> annotationType = key.getAnnotationType();
|
||||||
|
if (annotation != null && !isMarker(annotationType)) {
|
||||||
|
return key.getAnnotation().toString();
|
||||||
|
} else if (key.getAnnotationType() != null) {
|
||||||
|
return "@" + key.getAnnotationType().getName();
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for the presence of annotations. Caches results because Android doesn't.
|
* Checks for the presence of annotations. Caches results because Android doesn't.
|
||||||
*/
|
*/
|
||||||
|
@ -303,7 +347,8 @@ public class Annotations {
|
||||||
* Returns true if the given class has one of the desired annotations.
|
* Returns true if the given class has one of the desired annotations.
|
||||||
*/
|
*/
|
||||||
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
|
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
|
||||||
new CacheLoader<Class<? extends Annotation>, Boolean>() {
|
new CacheLoader<>() {
|
||||||
|
@Override
|
||||||
public Boolean load(Class<? extends Annotation> annotationType) {
|
public Boolean load(Class<? extends Annotation> annotationType) {
|
||||||
for (Annotation annotation : annotationType.getAnnotations()) {
|
for (Annotation annotation : annotationType.getAnnotations()) {
|
||||||
if (annotationTypes.contains(annotation.annotationType())) {
|
if (annotationTypes.contains(annotation.annotationType())) {
|
||||||
|
@ -314,8 +359,8 @@ public class Annotations {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
|
final LoadingCache<Class<? extends Annotation>, Boolean> cache =
|
||||||
.build(hasAnnotations);
|
CacheBuilder.newBuilder().weakKeys().build(hasAnnotations);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new checker that looks for annotations of the given types.
|
* Constructs a new checker that looks for annotations of the given types.
|
||||||
|
|
|
@ -29,24 +29,29 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
super(binder, elements, source, key);
|
super(binder, elements, source, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
annotatedWithInternal(annotationType);
|
annotatedWithInternal(annotationType);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
||||||
annotatedWithInternal(annotation);
|
annotatedWithInternal(annotation);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
||||||
return to(Key.get(implementation));
|
return to(Key.get(implementation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
||||||
return to(Key.get(implementation));
|
return to(Key.get(implementation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
||||||
checkNotNull(linkedKey, "linkedKey");
|
checkNotNull(linkedKey, "linkedKey");
|
||||||
checkNotTargetted();
|
checkNotTargetted();
|
||||||
|
@ -56,6 +61,7 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void toInstance(T instance) {
|
public void toInstance(T instance) {
|
||||||
checkNotTargetted();
|
checkNotTargetted();
|
||||||
|
|
||||||
|
@ -79,10 +85,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
||||||
return toProvider((javax.inject.Provider<T>) provider);
|
return toProvider((javax.inject.Provider<T>) provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
||||||
checkNotNull(provider, "provider");
|
checkNotNull(provider, "provider");
|
||||||
checkNotTargetted();
|
checkNotTargetted();
|
||||||
|
@ -102,16 +110,19 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(
|
public BindingBuilder<T> toProvider(
|
||||||
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||||
return toProvider(Key.get(providerType));
|
return toProvider(Key.get(providerType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(
|
public BindingBuilder<T> toProvider(
|
||||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||||
return toProvider(Key.get(providerType));
|
return toProvider(Key.get(providerType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(
|
public BindingBuilder<T> toProvider(
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||||
checkNotNull(providerKey, "providerKey");
|
checkNotNull(providerKey, "providerKey");
|
||||||
|
@ -123,10 +134,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
||||||
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
||||||
TypeLiteral<? extends S> type) {
|
TypeLiteral<? extends S> type) {
|
||||||
checkNotNull(constructor, "constructor");
|
checkNotNull(constructor, "constructor");
|
||||||
|
|
|
@ -34,14 +34,17 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
||||||
this.scoping = scoping;
|
this.scoping = scoping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Key<T> getKey() {
|
public Key<T> getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getSource() {
|
public Object getSource() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Provider<T> getProvider() {
|
public Provider<T> getProvider() {
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
if (injector == null) {
|
if (injector == null) {
|
||||||
|
@ -69,10 +72,12 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
||||||
return this instanceof InstanceBinding;
|
return this instanceof InstanceBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
||||||
return scoping.acceptVisitor(visitor);
|
return scoping.acceptVisitor(visitor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) {
|
return command.acceptTargetVisitor(new Processor<>((BindingImpl<T>) command) {
|
||||||
@Override
|
@Override
|
||||||
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
||||||
prepareBinding();
|
prepareBinding();
|
||||||
|
@ -74,11 +74,11 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
// the processor was constructed w/ it
|
// the processor was constructed w/ it
|
||||||
Initializable<T> ref = initializer.requestInjection(
|
Initializable<T> ref = initializer.requestInjection(
|
||||||
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
||||||
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
|
ConstantFactory<? extends T> factory = new ConstantFactory<>(ref);
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory =
|
||||||
= Scoping.scope(key, injector, factory, source, scoping);
|
Scoping.scope(key, injector, factory, source, scoping);
|
||||||
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
|
putBinding(new InstanceBindingImpl<>(injector, key, source,
|
||||||
instance));
|
scopedFactory, injectionPoints, instance));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,18 +86,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
||||||
prepareBinding();
|
prepareBinding();
|
||||||
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
|
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
|
||||||
|
if (provider instanceof InternalProviderInstanceBindingImpl.Factory) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<T> asProviderMethod =
|
||||||
|
(InternalProviderInstanceBindingImpl.Factory<T>) provider;
|
||||||
|
return visitInternalProviderInstanceBindingFactory(asProviderMethod);
|
||||||
|
}
|
||||||
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
||||||
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
initializer.requestInjection(injector, provider, null, source, injectionPoints);
|
||||||
injector, provider, null, source, injectionPoints);
|
|
||||||
// always visited with Binding<T>
|
// always visited with Binding<T>
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
|
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<>(
|
||||||
initializable, source,
|
initializable, source,
|
||||||
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory = Scoping.scope(key, injector, factory, source, scoping);
|
||||||
= Scoping.scope(key, injector, factory, source, scoping);
|
putBinding(new ProviderInstanceBindingImpl<>(injector, key, source, scopedFactory, scoping,
|
||||||
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
|
|
||||||
provider, injectionPoints));
|
provider, injectionPoints));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -108,13 +112,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
|
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
|
||||||
// always visited with Binding<T>
|
// always visited with Binding<T>
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
|
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<>(
|
||||||
injector, providerKey, source,
|
injector, providerKey, source,
|
||||||
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
||||||
bindingData.addCreationListener(boundProviderFactory);
|
bindingData.addCreationListener(boundProviderFactory);
|
||||||
InternalFactory<? extends T> scopedFactory = Scoping.scope(
|
InternalFactory<? extends T> scopedFactory = Scoping.scope(
|
||||||
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
|
key, injector, boundProviderFactory, source, scoping);
|
||||||
putBinding(new LinkedProviderBindingImpl<T>(
|
putBinding(new LinkedProviderBindingImpl<>(
|
||||||
injector, key, source, scopedFactory, scoping, providerKey));
|
injector, key, source, scopedFactory, scoping, providerKey));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -126,13 +130,36 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
if (key.equals(linkedKey)) {
|
if (key.equals(linkedKey)) {
|
||||||
errors.recursiveBinding();
|
errors.recursiveBinding();
|
||||||
}
|
}
|
||||||
|
FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
|
||||||
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
|
|
||||||
bindingData.addCreationListener(factory);
|
bindingData.addCreationListener(factory);
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory =
|
||||||
= Scoping.scope(key, injector, factory, source, scoping);
|
Scoping.scope(key, injector, factory, source, scoping);
|
||||||
putBinding(
|
putBinding(new LinkedBindingImpl<>(injector, key, source, scopedFactory, scoping, linkedKey));
|
||||||
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle ProviderMethods specially. */
|
||||||
|
private Boolean visitInternalProviderInstanceBindingFactory(
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<T> provider) {
|
||||||
|
InternalProviderInstanceBindingImpl<T> binding =
|
||||||
|
new InternalProviderInstanceBindingImpl<>(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
source,
|
||||||
|
provider,
|
||||||
|
Scoping.scope(key, injector, provider, source, scoping),
|
||||||
|
scoping);
|
||||||
|
switch (binding.getInitializationTiming()) {
|
||||||
|
case DELAYED:
|
||||||
|
scheduleDelayedInitialization(binding);
|
||||||
|
break;
|
||||||
|
case EAGER:
|
||||||
|
scheduleInitialization(binding);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
putBinding(binding);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +199,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
|
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
|
||||||
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
|
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<>(key, privateElements);
|
||||||
bindingData.addCreationListener(exposedKeyFactory);
|
bindingData.addCreationListener(exposedKeyFactory);
|
||||||
putBinding(new ExposedBindingImpl<T>(
|
putBinding(new ExposedBindingImpl<>(
|
||||||
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
|
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,17 @@ import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates to a custom factory which is also bound in the injector.
|
* Delegates to a custom factory which is also bound in the injector.
|
||||||
*/
|
*/
|
||||||
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
|
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
|
||||||
|
|
||||||
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||||
|
|
||||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
||||||
|
|
||||||
BoundProviderFactory(
|
BoundProviderFactory(
|
||||||
|
@ -24,11 +25,12 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
|
||||||
Object source,
|
Object source,
|
||||||
ProvisionListenerStackCallback<T> provisionCallback) {
|
ProvisionListenerStackCallback<T> provisionCallback) {
|
||||||
super(source);
|
super(source);
|
||||||
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
|
this.provisionCallback = provisionCallback;
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.providerKey = providerKey;
|
this.providerKey = providerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notify(Errors errors) {
|
public void notify(Errors errors) {
|
||||||
try {
|
try {
|
||||||
providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
||||||
|
@ -37,25 +39,27 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws InternalProvisionException {
|
||||||
context.pushState(providerKey, source);
|
context.pushState(providerKey, source);
|
||||||
try {
|
try {
|
||||||
errors = errors.withSource(providerKey);
|
javax.inject.Provider<? extends T> provider = providerFactory.get(context, dependency, true);
|
||||||
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
|
return circularGet(provider, context, dependency, provisionCallback);
|
||||||
return circularGet(provider, errors, context, dependency, provisionCallback);
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(providerKey);
|
||||||
} finally {
|
} finally {
|
||||||
context.popState();
|
context.popState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
|
||||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
return super.provision(provider, errors, dependency, constructionContext);
|
return super.provision(provider, dependency, constructionContext);
|
||||||
} catch (RuntimeException userException) {
|
} catch (RuntimeException userException) {
|
||||||
throw errors.errorInProvider(userException).toException();
|
throw InternalProvisionException.errorInProvider(userException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import com.google.inject.Key;
|
||||||
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
||||||
import com.google.inject.binder.ConstantBindingBuilder;
|
import com.google.inject.binder.ConstantBindingBuilder;
|
||||||
import com.google.inject.spi.Element;
|
import com.google.inject.spi.Element;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -24,56 +23,69 @@ public final class ConstantBindingBuilderImpl<T>
|
||||||
super(binder, elements, source, (Key<T>) NULL_KEY);
|
super(binder, elements, source, (Key<T>) NULL_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
annotatedWithInternal(annotationType);
|
annotatedWithInternal(annotationType);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
||||||
annotatedWithInternal(annotation);
|
annotatedWithInternal(annotation);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final String value) {
|
public void to(final String value) {
|
||||||
toConstant(String.class, value);
|
toConstant(String.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final int value) {
|
public void to(final int value) {
|
||||||
toConstant(Integer.class, value);
|
toConstant(Integer.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final long value) {
|
public void to(final long value) {
|
||||||
toConstant(Long.class, value);
|
toConstant(Long.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final boolean value) {
|
public void to(final boolean value) {
|
||||||
toConstant(Boolean.class, value);
|
toConstant(Boolean.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final double value) {
|
public void to(final double value) {
|
||||||
toConstant(Double.class, value);
|
toConstant(Double.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final float value) {
|
public void to(final float value) {
|
||||||
toConstant(Float.class, value);
|
toConstant(Float.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final short value) {
|
public void to(final short value) {
|
||||||
toConstant(Short.class, value);
|
toConstant(Short.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final char value) {
|
public void to(final char value) {
|
||||||
toConstant(Character.class, value);
|
toConstant(Character.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final byte value) {
|
public void to(final byte value) {
|
||||||
toConstant(Byte.class, value);
|
toConstant(Byte.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final Class<?> value) {
|
public void to(final Class<?> value) {
|
||||||
toConstant(Class.class, value);
|
toConstant(Class.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <E extends Enum<E>> void to(final E value) {
|
public <E extends Enum<E>> void to(final E value) {
|
||||||
toConstant(value.getDeclaringClass(), value);
|
toConstant(value.getDeclaringClass(), value);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +117,7 @@ public final class ConstantBindingBuilderImpl<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
setBinding(new InstanceBindingImpl<T>(
|
setBinding(new InstanceBindingImpl<T>(
|
||||||
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT));
|
base.getSource(), key, base.getScoping(), ImmutableSet.of(), instanceAsT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,15 +7,17 @@ final class ConstantFactory<T> implements InternalFactory<T> {
|
||||||
|
|
||||||
private final Initializable<T> initializable;
|
private final Initializable<T> initializable;
|
||||||
|
|
||||||
public ConstantFactory(Initializable<T> initializable) {
|
ConstantFactory(Initializable<T> initializable) {
|
||||||
this.initializable = initializable;
|
this.initializable = initializable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
return initializable.get(errors);
|
throws InternalProvisionException {
|
||||||
|
return initializable.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(ConstantFactory.class)
|
return MoreObjects.toStringHelper(ConstantFactory.class)
|
||||||
.add("value", initializable)
|
.add("value", initializable)
|
||||||
|
|
|
@ -41,26 +41,26 @@ final class ConstructionContext<T> {
|
||||||
invocationHandlers = null;
|
invocationHandlers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object createProxy(Errors errors, InjectorOptions injectorOptions,
|
public Object createProxy(InjectorOptions injectorOptions,
|
||||||
Class<?> expectedType) throws ErrorsException {
|
Class<?> expectedType) throws InternalProvisionException {
|
||||||
if (injectorOptions.disableCircularProxies) {
|
if (injectorOptions.disableCircularProxies) {
|
||||||
throw errors.circularProxiesDisabled(expectedType).toException();
|
throw InternalProvisionException.circularDependenciesDisabled(expectedType);
|
||||||
}
|
}
|
||||||
if (!expectedType.isInterface()) {
|
if (!expectedType.isInterface()) {
|
||||||
throw errors.cannotSatisfyCircularDependency(expectedType).toException();
|
throw InternalProvisionException.cannotProxyClass(expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invocationHandlers == null) {
|
if (invocationHandlers == null) {
|
||||||
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
|
invocationHandlers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
|
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<>();
|
||||||
invocationHandlers.add(invocationHandler);
|
invocationHandlers.add(invocationHandler);
|
||||||
|
|
||||||
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
||||||
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
||||||
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
||||||
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
new Class<?>[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProxyDelegates(T delegate) {
|
public void setProxyDelegates(T delegate) {
|
||||||
|
|
|
@ -26,10 +26,15 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
implements ConstructorBinding<T>, DelayedInitialize {
|
implements ConstructorBinding<T>, DelayedInitialize {
|
||||||
|
|
||||||
private final Factory<T> factory;
|
private final Factory<T> factory;
|
||||||
|
|
||||||
private final InjectionPoint constructorInjectionPoint;
|
private final InjectionPoint constructorInjectionPoint;
|
||||||
|
|
||||||
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
private ConstructorBindingImpl(InjectorImpl injector,
|
||||||
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
|
Key<T> key,
|
||||||
|
Object source,
|
||||||
|
InternalFactory<? extends T> scopedFactory,
|
||||||
|
Scoping scoping,
|
||||||
|
Factory<T> factory,
|
||||||
InjectionPoint constructorInjectionPoint) {
|
InjectionPoint constructorInjectionPoint) {
|
||||||
super(injector, key, source, scopedFactory, scoping);
|
super(injector, key, source, scopedFactory, scoping);
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
|
@ -53,7 +58,10 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
* only succeed if retrieved from a linked binding
|
* only succeed if retrieved from a linked binding
|
||||||
*/
|
*/
|
||||||
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
|
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
|
||||||
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
|
InjectionPoint constructorInjector,
|
||||||
|
Object source,
|
||||||
|
Scoping scoping,
|
||||||
|
Errors errors,
|
||||||
boolean failIfNotLinked, boolean failIfNotExplicit)
|
boolean failIfNotLinked, boolean failIfNotExplicit)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
int numErrors = errors.size();
|
int numErrors = errors.size();
|
||||||
|
@ -65,7 +73,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
|
|
||||||
// We can't inject abstract classes.
|
// We can't inject abstract classes.
|
||||||
if (Modifier.isAbstract(rawType.getModifiers())) {
|
if (Modifier.isAbstract(rawType.getModifiers())) {
|
||||||
errors.missingImplementation(key);
|
errors.missingImplementationWithHint(key, injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error: Inner class.
|
// Error: Inner class.
|
||||||
|
@ -110,7 +118,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
/**
|
/**
|
||||||
* Returns true if the inject annotation is on the constructor.
|
* Returns true if the inject annotation is on the constructor.
|
||||||
*/
|
*/
|
||||||
private static boolean hasAtInject(Constructor cxtor) {
|
private static boolean hasAtInject(Constructor<?> cxtor) {
|
||||||
return cxtor.isAnnotationPresent(Inject.class)
|
return cxtor.isAnnotationPresent(Inject.class)
|
||||||
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
|
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
|
||||||
}
|
}
|
||||||
|
@ -162,21 +170,25 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
return Dependency.forInjectionPoints(builder.build());
|
return Dependency.forInjectionPoints(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
checkState(factory.constructorInjector != null, "not initialized");
|
checkState(factory.constructorInjector != null, "not initialized");
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public InjectionPoint getConstructor() {
|
public InjectionPoint getConstructor() {
|
||||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||||
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<InjectionPoint> getInjectableMembers() {
|
public Set<InjectionPoint> getInjectableMembers() {
|
||||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||||
return factory.constructorInjector.getInjectableMembers();
|
return factory.constructorInjector.getInjectableMembers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
|
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
|
||||||
.add(getConstructor())
|
.add(getConstructor())
|
||||||
|
@ -241,18 +253,19 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
checkState(constructorInjector != null, "Constructor not ready");
|
ConstructorInjector<T> localInjector = constructorInjector;
|
||||||
|
if (localInjector == null) {
|
||||||
if (failIfNotLinked && !linked) {
|
throw new IllegalStateException("Constructor not ready");
|
||||||
throw errors.jitDisabled(key).toException();
|
}
|
||||||
|
if (!linked && failIfNotLinked) {
|
||||||
|
throw InternalProvisionException.jitDisabled(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This may not actually be safe because it could return a super type of T (if that's all the
|
// This may not actually be safe because it could return a super type of T (if that's all the
|
||||||
// client needs), but it should be OK in practice thanks to the wonders of erasure.
|
// client needs), but it should be OK in practice thanks to the wonders of erasure.
|
||||||
return (T) constructorInjector.construct(errors, context,
|
return (T) localInjector.construct(context, dependency, provisionCallback);
|
||||||
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -40,37 +41,39 @@ final class ConstructorInjector<T> {
|
||||||
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
||||||
* it may return a proxy.
|
* it may return a proxy.
|
||||||
*/
|
*/
|
||||||
Object construct(final Errors errors, final InternalContext context,
|
Object construct(final InternalContext context,
|
||||||
Class<?> expectedType,
|
Dependency<?> dependency,
|
||||||
ProvisionListenerStackCallback<T> provisionCallback)
|
ProvisionListenerStackCallback<T> provisionCallback)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||||
|
|
||||||
// We have a circular reference between constructors. Return a proxy.
|
// We have a circular reference between constructors. Return a proxy.
|
||||||
if (constructionContext.isConstructing()) {
|
if (constructionContext.isConstructing()) {
|
||||||
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
||||||
return constructionContext.createProxy(
|
return constructionContext.createProxy(
|
||||||
errors, context.getInjectorOptions(), expectedType);
|
context.getInjectorOptions(), dependency.getKey().getTypeLiteral().getRawType());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're re-entering this factory while injecting fields or methods,
|
// If we're re-entering this factory while injecting fields or methods,
|
||||||
// return the same instance. This prevents infinite loops.
|
// return the same instance. This prevents infinite loops.
|
||||||
T t = constructionContext.getCurrentReference();
|
T t = constructionContext.getCurrentReference();
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
|
if (context.getInjectorOptions().disableCircularProxies) {
|
||||||
|
throw InternalProvisionException.circularDependenciesDisabled(
|
||||||
|
dependency.getKey().getTypeLiteral().getRawType());
|
||||||
|
} else {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructionContext.startConstruction();
|
constructionContext.startConstruction();
|
||||||
try {
|
try {
|
||||||
// Optimization: Don't go through the callback stack if we have no listeners.
|
// Optimization: Don't go through the callback stack if we have no listeners.
|
||||||
if (!provisionCallback.hasListeners()) {
|
if (provisionCallback == null) {
|
||||||
return provision(errors, context, constructionContext);
|
return provision(context, constructionContext);
|
||||||
} else {
|
} else {
|
||||||
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
return provisionCallback.provision(context, () ->
|
||||||
public T call() throws ErrorsException {
|
provision(context, constructionContext));
|
||||||
return provision(errors, context, constructionContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
constructionContext.finishConstruction();
|
constructionContext.finishConstruction();
|
||||||
|
@ -80,12 +83,12 @@ final class ConstructorInjector<T> {
|
||||||
/**
|
/**
|
||||||
* Provisions a new T.
|
* Provisions a new T.
|
||||||
*/
|
*/
|
||||||
private T provision(Errors errors, InternalContext context,
|
private T provision(InternalContext context, ConstructionContext<T> constructionContext)
|
||||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
T t;
|
T t;
|
||||||
try {
|
try {
|
||||||
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
|
||||||
t = constructionProxy.newInstance(parameters);
|
t = constructionProxy.newInstance(parameters);
|
||||||
constructionContext.setProxyDelegates(t);
|
constructionContext.setProxyDelegates(t);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -94,17 +97,17 @@ final class ConstructorInjector<T> {
|
||||||
|
|
||||||
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
||||||
constructionContext.setCurrentReference(t);
|
constructionContext.setCurrentReference(t);
|
||||||
|
MembersInjectorImpl<T> localMembersInjector = membersInjector;
|
||||||
membersInjector.injectMembers(t, errors, context, false);
|
localMembersInjector.injectMembers(t, context, false);
|
||||||
membersInjector.notifyListeners(t, errors);
|
localMembersInjector.notifyListeners(t);
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
} catch (InvocationTargetException userException) {
|
} catch (InvocationTargetException userException) {
|
||||||
Throwable cause = userException.getCause() != null
|
Throwable cause = userException.getCause() != null
|
||||||
? userException.getCause()
|
? userException.getCause()
|
||||||
: userException;
|
: userException;
|
||||||
throw errors.withSource(constructionProxy.getInjectionPoint())
|
throw InternalProvisionException.errorInjectingConstructor(cause)
|
||||||
.errorInjectingConstructor(cause).toException();
|
.addSource(constructionProxy.getInjectionPoint());
|
||||||
} finally {
|
} finally {
|
||||||
constructionContext.removeCurrentReference();
|
constructionContext.removeCurrentReference();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import com.google.inject.spi.InjectionPoint;
|
||||||
final class ConstructorInjectorStore {
|
final class ConstructorInjectorStore {
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
|
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache =
|
||||||
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() {
|
new FailableCache<>() {
|
||||||
@Override
|
@Override
|
||||||
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
|
@ -46,12 +46,12 @@ final class ConstructorInjectorStore {
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
int numErrorsBefore = errors.size();
|
int numErrorsBefore = errors.size();
|
||||||
|
|
||||||
SingleParameterInjector<?>[] constructorParameterInjectors
|
SingleParameterInjector<?>[] constructorParameterInjectors =
|
||||||
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
||||||
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
|
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)
|
||||||
.get(injectionPoint.getDeclaringType(), errors);
|
injector.membersInjectorStore.get(injectionPoint.getDeclaringType(), errors);
|
||||||
|
|
||||||
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
|
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.google.inject.internal;
|
|
||||||
|
|
||||||
interface ContextualCallable<T> {
|
|
||||||
T call(InternalContext context) throws ErrorsException;
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Supplier;
|
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
|
@ -12,7 +11,6 @@ import com.google.common.collect.Multimaps;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
@ -43,7 +41,7 @@ interface CycleDetectingLock<ID> {
|
||||||
* In case no cycle is detected performance is O(threads creating singletons),
|
* In case no cycle is detected performance is O(threads creating singletons),
|
||||||
* in case cycle is detected performance is O(singleton locks).
|
* in case cycle is detected performance is O(singleton locks).
|
||||||
*/
|
*/
|
||||||
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle();
|
ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unlocks previously locked lock.
|
* Unlocks previously locked lock.
|
||||||
|
@ -82,7 +80,7 @@ interface CycleDetectingLock<ID> {
|
||||||
*
|
*
|
||||||
* Guarded by {@code this}.
|
* Guarded by {@code this}.
|
||||||
*/
|
*/
|
||||||
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread =
|
private static final Multimap<Thread, ReentrantCycleDetectingLock<?>> locksOwnedByThread =
|
||||||
LinkedHashMultimap.create();
|
LinkedHashMultimap.create();
|
||||||
/**
|
/**
|
||||||
* Specifies lock that thread is currently waiting on to own it.
|
* Specifies lock that thread is currently waiting on to own it.
|
||||||
|
@ -100,22 +98,22 @@ interface CycleDetectingLock<ID> {
|
||||||
*
|
*
|
||||||
* Guarded by {@code this}.
|
* Guarded by {@code this}.
|
||||||
*/
|
*/
|
||||||
private Map<Long, ReentrantCycleDetectingLock> lockThreadIsWaitingOn = Maps.newHashMap();
|
private static Map<Thread, ReentrantCycleDetectingLock<?>> lockThreadIsWaitingOn = Maps.newHashMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new lock within this factory context. We can guarantee that locks created by
|
* Creates new lock within this factory context. We can guarantee that locks created by
|
||||||
* the same factory would not deadlock.
|
* the same factory would not deadlock.
|
||||||
*
|
*
|
||||||
* @param newLockId lock id that would be used to report lock cycles if detected
|
* @param userLockId lock id that would be used to report lock cycles if detected
|
||||||
*/
|
*/
|
||||||
CycleDetectingLock<ID> create(ID newLockId) {
|
CycleDetectingLock<ID> create(ID userLockId) {
|
||||||
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock());
|
return new ReentrantCycleDetectingLock<>(this, userLockId, new ReentrantLock());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation for {@link CycleDetectingLock}.
|
* The implementation for {@link CycleDetectingLock}.
|
||||||
*/
|
*/
|
||||||
class ReentrantCycleDetectingLock implements CycleDetectingLock<ID> {
|
static class ReentrantCycleDetectingLock<ID> implements CycleDetectingLock<ID> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Underlying lock used for actual waiting when no potential deadlocks are detected.
|
* Underlying lock used for actual waiting when no potential deadlocks are detected.
|
||||||
|
@ -125,50 +123,56 @@ interface CycleDetectingLock<ID> {
|
||||||
* User id for this lock.
|
* User id for this lock.
|
||||||
*/
|
*/
|
||||||
private final ID userLockId;
|
private final ID userLockId;
|
||||||
|
/** Factory that was used to create this lock. */
|
||||||
|
private final CycleDetectingLockFactory<ID> lockFactory;
|
||||||
/**
|
/**
|
||||||
* Thread id for the thread that owned this lock. Nullable.
|
* Thread id for the thread that owned this lock. Nullable.
|
||||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||||
*/
|
*/
|
||||||
private Long lockOwnerThreadId = null;
|
private Thread lockOwnerThread = null;
|
||||||
/**
|
/**
|
||||||
* Number of times that thread owned this lock.
|
* Number of times that thread owned this lock.
|
||||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||||
*/
|
*/
|
||||||
private int lockReentranceCount = 0;
|
private int lockReentranceCount = 0;
|
||||||
|
|
||||||
ReentrantCycleDetectingLock(ID userLockId, Lock lockImplementation) {
|
ReentrantCycleDetectingLock(
|
||||||
|
CycleDetectingLockFactory<ID> lockFactory, ID userLockId, Lock lockImplementation) {
|
||||||
|
this.lockFactory = lockFactory;
|
||||||
this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
|
this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
|
||||||
this.lockImplementation = Preconditions.checkNotNull(
|
this.lockImplementation = Preconditions.checkNotNull(
|
||||||
lockImplementation, "lockImplementation");
|
lockImplementation, "lockImplementation");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() {
|
public ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle() {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
synchronized (CycleDetectingLockFactory.this) {
|
synchronized (CycleDetectingLockFactory.class) {
|
||||||
checkState();
|
checkState();
|
||||||
ListMultimap<Long, ID> locksInCycle = detectPotentialLocksCycle();
|
// Add this lock to the waiting map to ensure it is included in any reported lock cycle.
|
||||||
|
lockThreadIsWaitingOn.put(currentThread, this);
|
||||||
|
ListMultimap<Thread, ID> locksInCycle = detectPotentialLocksCycle();
|
||||||
if (!locksInCycle.isEmpty()) {
|
if (!locksInCycle.isEmpty()) {
|
||||||
|
// We aren't actually going to wait for this lock, so remove it from the map.
|
||||||
|
lockThreadIsWaitingOn.remove(currentThread);
|
||||||
// potential deadlock is found, we don't try to take this lock
|
// potential deadlock is found, we don't try to take this lock
|
||||||
return locksInCycle;
|
return locksInCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockThreadIsWaitingOn.put(currentThreadId, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this may be blocking, but we don't expect it to cause a deadlock
|
// this may be blocking, but we don't expect it to cause a deadlock
|
||||||
lockImplementation.lock();
|
lockImplementation.lock();
|
||||||
|
|
||||||
synchronized (CycleDetectingLockFactory.this) {
|
synchronized (CycleDetectingLockFactory.class) {
|
||||||
// current thread is no longer waiting on this lock
|
// current thread is no longer waiting on this lock
|
||||||
lockThreadIsWaitingOn.remove(currentThreadId);
|
lockThreadIsWaitingOn.remove(currentThread);
|
||||||
checkState();
|
checkState();
|
||||||
|
|
||||||
// mark it as owned by us
|
// mark it as owned by us
|
||||||
lockOwnerThreadId = currentThreadId;
|
lockOwnerThread = currentThread;
|
||||||
lockReentranceCount++;
|
lockReentranceCount++;
|
||||||
// add this lock to the list of locks owned by a current thread
|
// add this lock to the list of locks owned by a current thread
|
||||||
locksOwnedByThread.put(currentThreadId, this);
|
locksOwnedByThread.put(currentThread, this);
|
||||||
}
|
}
|
||||||
// no deadlock is found, locking successful
|
// no deadlock is found, locking successful
|
||||||
return ImmutableListMultimap.of();
|
return ImmutableListMultimap.of();
|
||||||
|
@ -176,12 +180,12 @@ interface CycleDetectingLock<ID> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlock() {
|
public void unlock() {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
synchronized (CycleDetectingLockFactory.this) {
|
synchronized (CycleDetectingLockFactory.class) {
|
||||||
checkState();
|
checkState();
|
||||||
Preconditions.checkState(lockOwnerThreadId != null,
|
Preconditions.checkState(lockOwnerThread != null,
|
||||||
"Thread is trying to unlock a lock that is not locked");
|
"Thread is trying to unlock a lock that is not locked");
|
||||||
Preconditions.checkState(lockOwnerThreadId == currentThreadId,
|
Preconditions.checkState(lockOwnerThread == currentThread,
|
||||||
"Thread is trying to unlock a lock owned by another thread");
|
"Thread is trying to unlock a lock owned by another thread");
|
||||||
|
|
||||||
// releasing underlying lock
|
// releasing underlying lock
|
||||||
|
@ -191,12 +195,12 @@ interface CycleDetectingLock<ID> {
|
||||||
lockReentranceCount--;
|
lockReentranceCount--;
|
||||||
if (lockReentranceCount == 0) {
|
if (lockReentranceCount == 0) {
|
||||||
// we no longer own this lock
|
// we no longer own this lock
|
||||||
lockOwnerThreadId = null;
|
lockOwnerThread = null;
|
||||||
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this),
|
Preconditions.checkState(locksOwnedByThread.remove(currentThread, this),
|
||||||
"Internal error: Can not find this lock in locks owned by a current thread");
|
"Internal error: Can not find this lock in locks owned by a current thread");
|
||||||
if (locksOwnedByThread.get(currentThreadId).isEmpty()) {
|
if (locksOwnedByThread.get(currentThread).isEmpty()) {
|
||||||
// clearing memory
|
// clearing memory
|
||||||
locksOwnedByThread.removeAll(currentThreadId);
|
locksOwnedByThread.removeAll(currentThread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,14 +210,14 @@ interface CycleDetectingLock<ID> {
|
||||||
* Check consistency of an internal state.
|
* Check consistency of an internal state.
|
||||||
*/
|
*/
|
||||||
void checkState() throws IllegalStateException {
|
void checkState() throws IllegalStateException {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId),
|
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThread),
|
||||||
"Internal error: Thread should not be in a waiting thread on a lock now");
|
"Internal error: Thread should not be in a waiting thread on a lock now");
|
||||||
if (lockOwnerThreadId != null) {
|
if (lockOwnerThread != null) {
|
||||||
// check state of a locked lock
|
// check state of a locked lock
|
||||||
Preconditions.checkState(lockReentranceCount >= 0,
|
Preconditions.checkState(lockReentranceCount >= 0,
|
||||||
"Internal error: Lock ownership and reentrance count internal states do not match");
|
"Internal error: Lock ownership and reentrance count internal states do not match");
|
||||||
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThreadId).contains(this),
|
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThread).contains(this),
|
||||||
"Internal error: Set of locks owned by a current thread and lock "
|
"Internal error: Set of locks owned by a current thread and lock "
|
||||||
+ "ownership status do not match");
|
+ "ownership status do not match");
|
||||||
} else {
|
} else {
|
||||||
|
@ -233,74 +237,80 @@ interface CycleDetectingLock<ID> {
|
||||||
*
|
*
|
||||||
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
||||||
*/
|
*/
|
||||||
private ListMultimap<Long, ID> detectPotentialLocksCycle() {
|
private ListMultimap<Thread, ID> detectPotentialLocksCycle() {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) {
|
if (lockOwnerThread == null || lockOwnerThread == currentThread) {
|
||||||
// if nobody owns this lock, lock cycle is impossible
|
// if nobody owns this lock, lock cycle is impossible
|
||||||
// if a current thread owns this lock, we let Guice to handle it
|
// if a current thread owns this lock, we let Guice to handle it
|
||||||
return ImmutableListMultimap.of();
|
return ImmutableListMultimap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
ListMultimap<Thread, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||||
new LinkedHashMap<Long, Collection<ID>>(),
|
new LinkedHashMap<>(), Lists::newArrayList);
|
||||||
new Supplier<List<ID>>() {
|
|
||||||
@Override
|
|
||||||
public List<ID> get() {
|
|
||||||
return Lists.newArrayList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// lock that is a part of a potential locks cycle, starts with current lock
|
// lock that is a part of a potential locks cycle, starts with current lock
|
||||||
ReentrantCycleDetectingLock lockOwnerWaitingOn = this;
|
ReentrantCycleDetectingLock<?> lockOwnerWaitingOn = this;
|
||||||
// try to find a dependency path between lock's owner thread and a current thread
|
// try to find a dependency path between lock's owner thread and a current thread
|
||||||
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) {
|
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThread != null) {
|
||||||
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId;
|
Thread threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThread;
|
||||||
// in case locks cycle exists lock we're waiting for is part of it
|
// in case locks cycle exists lock we're waiting for is part of it
|
||||||
potentialLocksCycle.putAll(threadOwnerThreadWaits,
|
lockOwnerWaitingOn =
|
||||||
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn));
|
addAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn, potentialLocksCycle);
|
||||||
|
if (threadOwnerThreadWaits == currentThread) {
|
||||||
if (threadOwnerThreadWaits == currentThreadId) {
|
|
||||||
// owner thread depends on current thread, cycle detected
|
// owner thread depends on current thread, cycle detected
|
||||||
return potentialLocksCycle;
|
return potentialLocksCycle;
|
||||||
}
|
}
|
||||||
// going for the next thread we wait on indirectly
|
|
||||||
lockOwnerWaitingOn = lockThreadIsWaitingOn.get(threadOwnerThreadWaits);
|
|
||||||
}
|
}
|
||||||
// no dependency path from an owner thread to a current thread
|
// no dependency path from an owner thread to a current thread
|
||||||
return ImmutableListMultimap.of();
|
return ImmutableListMultimap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return locks owned by a thread after a lock specified, inclusive.
|
* Adds all locks held by the given thread that are after the given lock and then returns the
|
||||||
|
* lock the thread is currently waiting on, if any
|
||||||
*/
|
*/
|
||||||
private List<ID> getAllLockIdsAfter(long threadId, ReentrantCycleDetectingLock lock) {
|
private ReentrantCycleDetectingLock<?> addAllLockIdsAfter(
|
||||||
List<ID> ids = Lists.newArrayList();
|
Thread thread,
|
||||||
|
ReentrantCycleDetectingLock<?> lock,
|
||||||
|
ListMultimap<Thread, ID> potentialLocksCycle) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId);
|
Collection<ReentrantCycleDetectingLock<?>> ownedLocks = locksOwnedByThread.get(thread);
|
||||||
Preconditions.checkNotNull(ownedLocks,
|
Preconditions.checkNotNull(ownedLocks,
|
||||||
"Internal error: No locks were found taken by a thread");
|
"Internal error: No locks were found taken by a thread");
|
||||||
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) {
|
for (ReentrantCycleDetectingLock<?> ownedLock : ownedLocks) {
|
||||||
if (ownedLock == lock) {
|
if (ownedLock == lock) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found && ownedLock.lockFactory == this.lockFactory) {
|
||||||
ids.add(ownedLock.userLockId);
|
// All locks are stored in a shared map therefore there is no way to
|
||||||
|
// enforce type safety. We know that our cast is valid as we check for a lock's
|
||||||
|
// factory. If the lock was generated by the
|
||||||
|
// same factory it has to have same type as the current lock.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ID userLockId = (ID) ownedLock.userLockId;
|
||||||
|
potentialLocksCycle.put(thread, userLockId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Preconditions.checkState(found, "Internal error: We can not find locks that "
|
Preconditions.checkState(found,
|
||||||
+ "created a cycle that we detected");
|
"Internal error: We can not find locks that created a cycle that we detected");
|
||||||
return ids;
|
ReentrantCycleDetectingLock<?> unownedLock = lockThreadIsWaitingOn.get(thread);
|
||||||
|
// If this thread is waiting for a lock add it to the cycle and return it
|
||||||
|
if (unownedLock != null && unownedLock.lockFactory == this.lockFactory) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ID typed = (ID) unownedLock.userLockId;
|
||||||
|
potentialLocksCycle.put(thread, typed);
|
||||||
|
}
|
||||||
|
return unownedLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
// copy is made to prevent a data race
|
// copy is made to prevent a data race
|
||||||
// no synchronization is used, potentially stale data, should be good enough
|
// no synchronization is used, potentially stale data, should be good enough
|
||||||
Long localLockOwnerThreadId = this.lockOwnerThreadId;
|
Thread thread = this.lockOwnerThread;
|
||||||
if (localLockOwnerThreadId != null) {
|
if (thread != null) {
|
||||||
return String.format("CycleDetectingLock[%s][locked by %s]",
|
return String.format("%s[%s][locked by %s]", super.toString(), userLockId, thread);
|
||||||
userLockId, localLockOwnerThreadId);
|
|
||||||
} else {
|
} else {
|
||||||
return String.format("CycleDetectingLock[%s][unlocked]", userLockId);
|
return String.format("%s[%s][unlocked]", super.toString(), userLockId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ComparisonChain;
|
||||||
|
import com.google.common.collect.Ordering;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for retrieving declared fields or methods in a particular order, because the JVM
|
||||||
|
* doesn't guarantee ordering for listing declared methods. We don't externally guarantee an
|
||||||
|
* ordering, but having a consistent ordering allows deterministic behavior and simpler tests.
|
||||||
|
*/
|
||||||
|
public final class DeclaredMembers {
|
||||||
|
|
||||||
|
private DeclaredMembers() {}
|
||||||
|
|
||||||
|
public static Field[] getDeclaredFields(Class<?> type) {
|
||||||
|
Field[] fields = type.getDeclaredFields();
|
||||||
|
Arrays.sort(fields, FIELD_ORDERING);
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Method[] getDeclaredMethods(Class<?> type) {
|
||||||
|
Method[] methods = type.getDeclaredMethods();
|
||||||
|
Arrays.sort(methods, METHOD_ORDERING);
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ordering suitable for comparing two classes if they are loaded by the same classloader
|
||||||
|
*
|
||||||
|
* <p>Within a single classloader there can only be one class with a given name, so we just
|
||||||
|
* compare the names.
|
||||||
|
*/
|
||||||
|
private static final Ordering<Class<?>> CLASS_ORDERING =
|
||||||
|
new Ordering<>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Class<?> o1, Class<?> o2) {
|
||||||
|
return o1.getName().compareTo(o2.getName());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ordering suitable for comparing two fields if they are owned by the same class.
|
||||||
|
*
|
||||||
|
* <p>Within a single class it is sufficent to compare the non-generic field signature which
|
||||||
|
* consists of the field name and type.
|
||||||
|
*/
|
||||||
|
private static final Ordering<Field> FIELD_ORDERING =
|
||||||
|
new Ordering<>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Field left, Field right) {
|
||||||
|
return ComparisonChain.start()
|
||||||
|
.compare(left.getName(), right.getName())
|
||||||
|
.compare(left.getType(), right.getType(), CLASS_ORDERING)
|
||||||
|
.result();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ordering suitable for comparing two methods if they are owned by the same class.
|
||||||
|
*
|
||||||
|
* <p>Within a single class it is sufficient to compare the non-generic method signature which
|
||||||
|
* consists of the name, return type and parameter types.
|
||||||
|
*/
|
||||||
|
private static final Ordering<Method> METHOD_ORDERING =
|
||||||
|
new Ordering<>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Method left, Method right) {
|
||||||
|
return ComparisonChain.start()
|
||||||
|
.compare(left.getName(), right.getName())
|
||||||
|
.compare(left.getReturnType(), right.getReturnType(), CLASS_ORDERING)
|
||||||
|
.compare(
|
||||||
|
Arrays.asList(left.getParameterTypes()),
|
||||||
|
Arrays.asList(right.getParameterTypes()),
|
||||||
|
CLASS_ORDERING.lexicographical())
|
||||||
|
.result();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -24,13 +24,8 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
|
||||||
@SuppressWarnings("unchecked") // the injection point is for a constructor of T
|
@SuppressWarnings("unchecked") // the injection point is for a constructor of T
|
||||||
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
||||||
|
|
||||||
// Use FastConstructor if the constructor is public.
|
if (!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) ||
|
||||||
if (Modifier.isPublic(constructor.getModifiers())) {
|
!Modifier.isPublic(constructor.getModifiers())) {
|
||||||
Class<T> classToConstruct = constructor.getDeclaringClass();
|
|
||||||
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,17 +33,17 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
|
||||||
public T newInstance(Object... arguments) throws InvocationTargetException {
|
public T newInstance(Object... arguments) throws InvocationTargetException {
|
||||||
try {
|
try {
|
||||||
return constructor.newInstance(arguments);
|
return constructor.newInstance(arguments);
|
||||||
} catch (InstantiationException e) {
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
throw new AssertionError(e); // shouldn't happen, we know this is a concrete type
|
throw new AssertionError(e); // shouldn't happen, we know this is a concrete type
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public InjectionPoint getInjectionPoint() {
|
public InjectionPoint getInjectionPoint() {
|
||||||
return injectionPoint;
|
return injectionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Constructor<T> getConstructor() {
|
public Constructor<T> getConstructor() {
|
||||||
return constructor;
|
return constructor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ final class DeferredLookups implements Lookups {
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Provider<T> getProvider(Key<T> key) {
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
ProviderLookup<T> lookup = new ProviderLookup<T>(key, key);
|
ProviderLookup<T> lookup = new ProviderLookup<>(key, key);
|
||||||
lookups.add(lookup);
|
lookups.add(lookup);
|
||||||
return lookup.getProvider();
|
return lookup.getProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
||||||
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<T>(type, type);
|
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<>(type, type);
|
||||||
lookups.add(lookup);
|
lookups.add(lookup);
|
||||||
return lookup.getMembersInjector();
|
return lookup.getMembersInjector();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.google.inject.multibindings;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
import com.google.inject.BindingAnnotation;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
MAPBINDER,
|
MAPBINDER,
|
||||||
MULTIBINDER,
|
MULTIBINDER;
|
||||||
OPTIONALBINDER;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,6 +43,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
: ImmutableSet.copyOf(injectionListeners);
|
: ImmutableSet.copyOf(injectionListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void register(MembersInjector<? super T> membersInjector) {
|
public void register(MembersInjector<? super T> membersInjector) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
membersInjectors.add(membersInjector);
|
membersInjectors.add(membersInjector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void register(InjectionListener<? super T> injectionListener) {
|
public void register(InjectionListener<? super T> injectionListener) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
|
||||||
|
@ -63,35 +65,42 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
injectionListeners.add(injectionListener);
|
injectionListeners.add(injectionListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addError(String message, Object... arguments) {
|
public void addError(String message, Object... arguments) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
errors.addMessage(message, arguments);
|
errors.addMessage(message, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addError(Throwable t) {
|
public void addError(Throwable t) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addError(Message message) {
|
public void addError(Message message) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
errors.addMessage(message);
|
errors.addMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Key<T> key) {
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
return lookups.getProvider(key);
|
return lookups.getProvider(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Class<T> type) {
|
public <T> Provider<T> getProvider(Class<T> type) {
|
||||||
return getProvider(Key.get(type));
|
return getProvider(Key.get(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
return lookups.getMembersInjector(typeLiteral);
|
return lookups.getMembersInjector(typeLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
return getMembersInjector(TypeLiteral.get(type));
|
return getMembersInjector(TypeLiteral.get(type));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,41 +4,33 @@ 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.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
|
import com.google.common.primitives.Primitives;
|
||||||
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.ConfigurationException;
|
import com.google.inject.ConfigurationException;
|
||||||
import com.google.inject.CreationException;
|
import com.google.inject.CreationException;
|
||||||
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.MembersInjector;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.Provides;
|
|
||||||
import com.google.inject.ProvisionException;
|
import com.google.inject.ProvisionException;
|
||||||
import com.google.inject.Scope;
|
import com.google.inject.Scope;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.internal.util.Classes;
|
|
||||||
import com.google.inject.internal.util.SourceProvider;
|
import com.google.inject.internal.util.SourceProvider;
|
||||||
import com.google.inject.internal.util.StackTraceElements;
|
|
||||||
import com.google.inject.spi.Dependency;
|
|
||||||
import com.google.inject.spi.ElementSource;
|
|
||||||
import com.google.inject.spi.InjectionListener;
|
|
||||||
import com.google.inject.spi.InjectionPoint;
|
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
import com.google.inject.spi.ScopeBinding;
|
import com.google.inject.spi.ScopeBinding;
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
import com.google.inject.spi.TypeListenerBinding;
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of error messages. If this type is passed as a method parameter, the method is
|
* A collection of error messages. If this type is passed as a method parameter, the method is
|
||||||
|
@ -56,43 +48,40 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
*/
|
*/
|
||||||
public final class Errors {
|
public final class Errors {
|
||||||
|
|
||||||
private static final Set<Dependency<?>> warnedDependencies =
|
/** When a binding is not found, show at most this many bindings with the same type */
|
||||||
Collections.newSetFromMap(new ConcurrentHashMap<Dependency<?>, Boolean>());
|
private static final int MAX_MATCHING_TYPES_REPORTED = 3;
|
||||||
private static final String CONSTRUCTOR_RULES =
|
|
||||||
"Classes must have either one (and only one) constructor "
|
/** When a binding is not found, show at most this many bindings that have some similarities */
|
||||||
+ "annotated with @Inject or a zero-argument constructor that is not private.";
|
private static final int MAX_RELATED_TYPES_REPORTED = 3;
|
||||||
private static final Collection<Converter<?>> converters = ImmutableList.of(
|
|
||||||
new Converter<Class>(Class.class) {
|
static <T> T checkNotNull(T reference, String name) {
|
||||||
@Override
|
if (reference != null) {
|
||||||
public String toString(Class c) {
|
return reference;
|
||||||
return c.getName();
|
|
||||||
}
|
}
|
||||||
},
|
NullPointerException npe = new NullPointerException(name);
|
||||||
new Converter<Member>(Member.class) {
|
throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
|
||||||
@Override
|
|
||||||
public String toString(Member member) {
|
|
||||||
return Classes.toString(member);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
new Converter<Key>(Key.class) {
|
static void checkConfiguration(boolean condition, String format, Object... args) {
|
||||||
@Override
|
if (condition) {
|
||||||
public String toString(Key key) {
|
return;
|
||||||
if (key.getAnnotationType() != null) {
|
|
||||||
return key.getTypeLiteral() + " annotated with "
|
|
||||||
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
|
|
||||||
} else {
|
|
||||||
return key.getTypeLiteral().toString();
|
|
||||||
}
|
}
|
||||||
|
throw new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
/**
|
private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
|
||||||
* The root errors object. Used to access the list of error messages.
|
ImmutableSet.<Class<?>>builder()
|
||||||
*/
|
.add(Object.class)
|
||||||
|
.add(String.class)
|
||||||
|
.addAll(Primitives.allWrapperTypes())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/** The root errors object. Used to access the list of error messages. */
|
||||||
private final Errors root;
|
private final Errors root;
|
||||||
/**
|
|
||||||
* The parent errors object. Used to obtain the chain of source objects.
|
/** The parent errors object. Used to obtain the chain of source objects. */
|
||||||
*/
|
|
||||||
private final Errors parent;
|
private final Errors parent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The leaf source for errors added here.
|
* The leaf source for errors added here.
|
||||||
*/
|
*/
|
||||||
|
@ -120,246 +109,101 @@ public final class Errors {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
|
/** Returns an instance that uses {@code source} as a reference point for newly added errors. */
|
||||||
if (throwable instanceof ProvisionException) {
|
|
||||||
return ((ProvisionException) throwable).getErrorMessages();
|
|
||||||
} else if (throwable instanceof ConfigurationException) {
|
|
||||||
return ((ConfigurationException) throwable).getErrorMessages();
|
|
||||||
} else if (throwable instanceof CreationException) {
|
|
||||||
return ((CreationException) throwable).getErrorMessages();
|
|
||||||
} else {
|
|
||||||
return ImmutableSet.of();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String format(String messageFormat, Object... arguments) {
|
|
||||||
for (int i = 0; i < arguments.length; i++) {
|
|
||||||
arguments[i] = Errors.convert(arguments[i]);
|
|
||||||
}
|
|
||||||
return String.format(messageFormat, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the formatted message for an exception with the specified messages.
|
|
||||||
*/
|
|
||||||
public static String format(String heading, Collection<Message> errorMessages) {
|
|
||||||
Formatter fmt = new Formatter().format(heading).format(":%n%n");
|
|
||||||
int index = 1;
|
|
||||||
boolean displayCauses = getOnlyCause(errorMessages) == null;
|
|
||||||
|
|
||||||
for (Message errorMessage : errorMessages) {
|
|
||||||
fmt.format("%s) %s%n", index++, errorMessage.getMessage());
|
|
||||||
|
|
||||||
List<Object> dependencies = errorMessage.getSources();
|
|
||||||
for (int i = dependencies.size() - 1; i >= 0; i--) {
|
|
||||||
Object source = dependencies.get(i);
|
|
||||||
formatSource(fmt, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
Throwable cause = errorMessage.getCause();
|
|
||||||
if (displayCauses && cause != null) {
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
cause.printStackTrace(new PrintWriter(writer));
|
|
||||||
fmt.format("Caused by: %s", writer.getBuffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.format("%n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessages.size() == 1) {
|
|
||||||
fmt.format("1 error");
|
|
||||||
} else {
|
|
||||||
fmt.format("%s errors", errorMessages.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
|
|
||||||
* zero or multiple messages with causes, null is returned.
|
|
||||||
*/
|
|
||||||
public static Throwable getOnlyCause(Collection<Message> messages) {
|
|
||||||
Throwable onlyCause = null;
|
|
||||||
for (Message message : messages) {
|
|
||||||
Throwable messageCause = message.getCause();
|
|
||||||
if (messageCause == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onlyCause != null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
onlyCause = messageCause;
|
|
||||||
}
|
|
||||||
|
|
||||||
return onlyCause;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object convert(Object o) {
|
|
||||||
ElementSource source = null;
|
|
||||||
if (o instanceof ElementSource) {
|
|
||||||
source = (ElementSource) o;
|
|
||||||
o = source.getDeclaringSource();
|
|
||||||
}
|
|
||||||
return convert(o, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object convert(Object o, ElementSource source) {
|
|
||||||
for (Converter<?> converter : converters) {
|
|
||||||
if (converter.appliesTo(o)) {
|
|
||||||
return appendModules(converter.convert(o), source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return appendModules(o, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object appendModules(Object source, ElementSource elementSource) {
|
|
||||||
String modules = moduleSourceString(elementSource);
|
|
||||||
if (modules.length() == 0) {
|
|
||||||
return source;
|
|
||||||
} else {
|
|
||||||
return source + modules;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String moduleSourceString(ElementSource elementSource) {
|
|
||||||
// if we only have one module (or don't know what they are), then don't bother
|
|
||||||
// reporting it, because the source already is going to report exactly that module.
|
|
||||||
if (elementSource == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
|
|
||||||
// Insert any original element sources w/ module info into the path.
|
|
||||||
while (elementSource.getOriginalElementSource() != null) {
|
|
||||||
elementSource = elementSource.getOriginalElementSource();
|
|
||||||
modules.addAll(0, elementSource.getModuleClassNames());
|
|
||||||
}
|
|
||||||
if (modules.size() <= 1) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ideally we'd do:
|
|
||||||
// return Joiner.on(" -> ")
|
|
||||||
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
|
|
||||||
// .append(")").toString();
|
|
||||||
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
|
|
||||||
StringBuilder builder = new StringBuilder(" (via modules: ");
|
|
||||||
for (int i = modules.size() - 1; i >= 0; i--) {
|
|
||||||
builder.append(modules.get(i));
|
|
||||||
if (i != 0) {
|
|
||||||
builder.append(" -> ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.append(")");
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void formatSource(Formatter formatter, Object source) {
|
|
||||||
ElementSource elementSource = null;
|
|
||||||
if (source instanceof ElementSource) {
|
|
||||||
elementSource = (ElementSource) source;
|
|
||||||
source = elementSource.getDeclaringSource();
|
|
||||||
}
|
|
||||||
formatSource(formatter, source, elementSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
|
|
||||||
String modules = moduleSourceString(elementSource);
|
|
||||||
if (source instanceof Dependency) {
|
|
||||||
Dependency<?> dependency = (Dependency<?>) source;
|
|
||||||
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
|
||||||
if (injectionPoint != null) {
|
|
||||||
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
|
|
||||||
} else {
|
|
||||||
formatSource(formatter, dependency.getKey(), elementSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (source instanceof InjectionPoint) {
|
|
||||||
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
|
|
||||||
|
|
||||||
} else if (source instanceof Class) {
|
|
||||||
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
|
|
||||||
|
|
||||||
} else if (source instanceof Member) {
|
|
||||||
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
|
|
||||||
|
|
||||||
} else if (source instanceof TypeLiteral) {
|
|
||||||
formatter.format(" while locating %s%s%n", source, modules);
|
|
||||||
|
|
||||||
} else if (source instanceof Key) {
|
|
||||||
Key<?> key = (Key<?>) source;
|
|
||||||
formatter.format(" while locating %s%n", convert(key, elementSource));
|
|
||||||
|
|
||||||
} else if (source instanceof Thread) {
|
|
||||||
formatter.format(" in thread %s%n", source);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
formatter.format(" at %s%s%n", source, modules);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
|
|
||||||
InjectionPoint injectionPoint, ElementSource elementSource) {
|
|
||||||
Member member = injectionPoint.getMember();
|
|
||||||
Class<? extends Member> memberType = Classes.memberType(member);
|
|
||||||
|
|
||||||
if (memberType == Field.class) {
|
|
||||||
dependency = injectionPoint.getDependencies().get(0);
|
|
||||||
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
|
|
||||||
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
|
|
||||||
|
|
||||||
} else if (dependency != null) {
|
|
||||||
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
|
|
||||||
formatter.format(" for parameter %s at %s%n",
|
|
||||||
dependency.getParameterIndex(), StackTraceElements.forMember(member));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
formatSource(formatter, injectionPoint.getMember());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an instance that uses {@code source} as a reference point for newly added errors.
|
|
||||||
*/
|
|
||||||
public Errors withSource(Object source) {
|
public Errors withSource(Object source) {
|
||||||
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
|
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
|
||||||
? this
|
? this
|
||||||
: new Errors(this, source);
|
: new Errors(this, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Errors missingImplementation(Key<?> key) {
|
||||||
* We use a fairly generic error message here. The motivation is to share the
|
|
||||||
* same message for both bind time errors:
|
|
||||||
* <pre><code>Guice.createInjector(new AbstractModule() {
|
|
||||||
* public void configure() {
|
|
||||||
* bind(Runnable.class);
|
|
||||||
* }
|
|
||||||
* }</code></pre>
|
|
||||||
* ...and at provide-time errors:
|
|
||||||
* <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
|
|
||||||
* Otherwise we need to know who's calling when resolving a just-in-time
|
|
||||||
* binding, which makes things unnecessarily complex.
|
|
||||||
*/
|
|
||||||
public Errors missingImplementation(Key key) {
|
|
||||||
return addMessage("No implementation for %s was bound.", key);
|
return addMessage("No implementation for %s was bound.", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors jitDisabled(Key key) {
|
/** Within guice's core, allow for better missing binding messages */
|
||||||
|
<T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(Messages.format("No implementation for %s was bound.", key));
|
||||||
|
|
||||||
|
// Keys which have similar strings as the desired key
|
||||||
|
List<String> possibleMatches = new ArrayList<>();
|
||||||
|
|
||||||
|
// Check for other keys that may have the same type,
|
||||||
|
// but not the same annotation
|
||||||
|
TypeLiteral<T> type = key.getTypeLiteral();
|
||||||
|
List<Binding<T>> sameTypes = injector.findBindingsByType(type);
|
||||||
|
if (!sameTypes.isEmpty()) {
|
||||||
|
sb.append(Messages.format("%n Did you mean?"));
|
||||||
|
int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
|
||||||
|
for (int i = 0; i < howMany; ++i) {
|
||||||
|
// TODO: Look into a better way to prioritize suggestions. For example, possbily
|
||||||
|
// use levenshtein distance of the given annotation vs actual annotation.
|
||||||
|
sb.append(Messages.format("%n * %s", sameTypes.get(i).getKey()));
|
||||||
|
}
|
||||||
|
int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
|
||||||
|
if (remaining > 0) {
|
||||||
|
String plural = (remaining == 1) ? "" : "s";
|
||||||
|
sb.append(Messages.format("%n %d more binding%s with other annotations.", remaining, plural));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For now, do a simple substring search for possibilities. This can help spot
|
||||||
|
// issues when there are generics being used (such as a wrapper class) and the
|
||||||
|
// user has forgotten they need to bind based on the wrapper, not the underlying
|
||||||
|
// class. In the future, consider doing a strict in-depth type search.
|
||||||
|
// TODO: Look into a better way to prioritize suggestions. For example, possbily
|
||||||
|
// use levenshtein distance of the type literal strings.
|
||||||
|
String want = type.toString();
|
||||||
|
Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
|
||||||
|
for (Key<?> bindingKey : bindingMap.keySet()) {
|
||||||
|
String have = bindingKey.getTypeLiteral().toString();
|
||||||
|
if (have.contains(want) || want.contains(have)) {
|
||||||
|
Formatter fmt = new Formatter();
|
||||||
|
Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
|
||||||
|
String match = String.format("%s bound%s", Messages.convert(bindingKey), fmt.toString());
|
||||||
|
possibleMatches.add(match);
|
||||||
|
// TODO: Consider a check that if there are more than some number of results,
|
||||||
|
// don't suggest any.
|
||||||
|
if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
|
||||||
|
// Early exit if we have found more than we need.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
|
||||||
|
sb.append(Messages.format("%n Did you mean?"));
|
||||||
|
for (String possibleMatch : possibleMatches) {
|
||||||
|
sb.append(Messages.format("%n %s", possibleMatch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If where are no possibilities to suggest, then handle the case of missing
|
||||||
|
// annotations on simple types. This is usually a bad idea.
|
||||||
|
if (sameTypes.isEmpty()
|
||||||
|
&& possibleMatches.isEmpty()
|
||||||
|
&& key.getAnnotationType() == null
|
||||||
|
&& COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
|
||||||
|
// We don't recommend using such simple types without annotations.
|
||||||
|
sb.append(Messages.format("%nThe key seems very generic, did you forget an annotation?"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return addMessage(sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors jitDisabled(Key<?> key) {
|
||||||
return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
|
return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors jitDisabledInParent(Key<?> key) {
|
public Errors jitDisabledInParent(Key<?> key) {
|
||||||
return addMessage(
|
return addMessage("Explicit bindings are required and %s would be bound in a parent injector.%n"
|
||||||
"Explicit bindings are required and %s would be bound in a parent injector.%n"
|
|
||||||
+ "Please add an explicit binding for it, either in the child or the parent.",
|
+ "Please add an explicit binding for it, either in the child or the parent.",
|
||||||
key);
|
key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors atInjectRequired(Class clazz) {
|
public Errors atInjectRequired(Class<?> clazz) {
|
||||||
return addMessage(
|
return addMessage("Explicit @Inject annotations are required on constructors,"
|
||||||
"Explicit @Inject annotations are required on constructors,"
|
|
||||||
+ " but %s has no constructors annotated with @Inject.",
|
+ " but %s has no constructors annotated with @Inject.",
|
||||||
clazz);
|
clazz);
|
||||||
}
|
}
|
||||||
|
@ -368,7 +212,7 @@ public final class Errors {
|
||||||
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
|
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
|
||||||
return addMessage("Received null converting '%s' (bound at %s) to %s%n"
|
return addMessage("Received null converting '%s' (bound at %s) to %s%n"
|
||||||
+ " using %s.",
|
+ " using %s.",
|
||||||
stringValue, convert(source), type, typeConverterBinding);
|
stringValue, Messages.convert(source), type, typeConverterBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type,
|
public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type,
|
||||||
|
@ -376,7 +220,7 @@ public final class Errors {
|
||||||
return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n"
|
return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n"
|
||||||
+ " using %s.%n"
|
+ " using %s.%n"
|
||||||
+ " Converter returned %s.",
|
+ " Converter returned %s.",
|
||||||
stringValue, convert(source), type, typeConverterBinding, converted);
|
stringValue, Messages.convert(source), type, typeConverterBinding, converted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors conversionError(String stringValue, Object source,
|
public Errors conversionError(String stringValue, Object source,
|
||||||
|
@ -384,7 +228,7 @@ public final class Errors {
|
||||||
return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n"
|
return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n"
|
||||||
+ " using %s.%n"
|
+ " using %s.%n"
|
||||||
+ " Reason: %s",
|
+ " Reason: %s",
|
||||||
stringValue, convert(source), type, typeConverterBinding, cause);
|
stringValue, Messages.convert(source), type, typeConverterBinding, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
|
public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
|
||||||
|
@ -393,18 +237,13 @@ public final class Errors {
|
||||||
+ " %s and%n"
|
+ " %s and%n"
|
||||||
+ " %s.%n"
|
+ " %s.%n"
|
||||||
+ " Please adjust your type converter configuration to avoid overlapping matches.",
|
+ " Please adjust your type converter configuration to avoid overlapping matches.",
|
||||||
stringValue, convert(source), type, a, b);
|
stringValue, Messages.convert(source), type, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors bindingToProvider() {
|
public Errors bindingToProvider() {
|
||||||
return addMessage("Binding to Provider is not allowed.");
|
return addMessage("Binding to Provider is not allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors subtypeNotProvided(Class<? extends Provider<?>> providerType,
|
|
||||||
Class<?> type) {
|
|
||||||
return addMessage("%s doesn't provide instances of %s.", providerType, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors notASubtype(Class<?> implementationType, Class<?> type) {
|
public Errors notASubtype(Class<?> implementationType, Class<?> type) {
|
||||||
return addMessage("%s doesn't extend %s.", implementationType, type);
|
return addMessage("%s doesn't extend %s.", implementationType, type);
|
||||||
}
|
}
|
||||||
|
@ -418,14 +257,14 @@ public final class Errors {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
|
public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
|
||||||
return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
|
return addMessage(Messages.format("Please annotate %s with @Retention(RUNTIME).", annotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
|
public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
|
||||||
return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
|
return addMessage(Messages.format("Please annotate %s with @ScopeAnnotation.", annotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors optionalConstructor(Constructor constructor) {
|
public Errors optionalConstructor(Constructor<?> constructor) {
|
||||||
return addMessage("%s is annotated @Inject(optional=true), "
|
return addMessage("%s is annotated @Inject(optional=true), "
|
||||||
+ "but constructors cannot be optional.", constructor);
|
+ "but constructors cannot be optional.", constructor);
|
||||||
}
|
}
|
||||||
|
@ -441,7 +280,7 @@ public final class Errors {
|
||||||
public Errors scopeAnnotationOnAbstractType(
|
public Errors scopeAnnotationOnAbstractType(
|
||||||
Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
|
Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
|
||||||
return addMessage("%s is annotated with %s, but scope annotations are not supported "
|
return addMessage("%s is annotated with %s, but scope annotations are not supported "
|
||||||
+ "for abstract types.%n Bound at %s.", type, scopeAnnotation, convert(source));
|
+ "for abstract types.%n Bound at %s.", type, scopeAnnotation, Messages.convert(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
|
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
|
||||||
|
@ -449,14 +288,27 @@ public final class Errors {
|
||||||
+ "to its parameters instead.", member, bindingAnnotation);
|
+ "to its parameters instead.", member, bindingAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors missingConstructor(Class<?> implementation) {
|
private static final String CONSTRUCTOR_RULES =
|
||||||
return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES,
|
"Injectable classes must have either one (and only one) constructor annotated with @Inject"
|
||||||
implementation);
|
+ " or a zero-argument constructor that is not private.";
|
||||||
|
|
||||||
|
public Errors missingConstructor(TypeLiteral<?> type) {
|
||||||
|
// Don't bother including the type in the message twice, unless the type is generic (i.e. the
|
||||||
|
// type has generics that the raw class loses)
|
||||||
|
String typeString = type.toString();
|
||||||
|
String rawTypeString = MoreTypes.getRawType(type.getType()).getName();
|
||||||
|
return addMessage(
|
||||||
|
"No implementation for %s (with no qualifier annotation) was bound, and could not find an"
|
||||||
|
+ " injectable constructor%s. %s",
|
||||||
|
typeString,
|
||||||
|
typeString.equals(rawTypeString) ? "" : " in " + rawTypeString,
|
||||||
|
CONSTRUCTOR_RULES);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors tooManyConstructors(Class<?> implementation) {
|
public Errors tooManyConstructors(Class<?> implementation) {
|
||||||
return addMessage("%s has more than one constructor annotated with @Inject. "
|
return addMessage(
|
||||||
+ CONSTRUCTOR_RULES, implementation);
|
"%s has more than one constructor annotated with @Inject. %s",
|
||||||
|
implementation, CONSTRUCTOR_RULES);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
|
public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
|
||||||
|
@ -518,7 +370,7 @@ public final class Errors {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors bindingAlreadySet(Key<?> key, Object source) {
|
public Errors bindingAlreadySet(Key<?> key, Object source) {
|
||||||
return addMessage("A binding to %s was already configured at %s.", key, convert(source));
|
return addMessage("A binding to %s was already configured at %s.", key, Messages.convert(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors jitBindingAlreadySet(Key<?> key) {
|
public Errors jitBindingAlreadySet(Key<?> key) {
|
||||||
|
@ -534,24 +386,19 @@ public final class Errors {
|
||||||
allSources.format("%n bound at %s", source);
|
allSources.format("%n bound at %s", source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Errors errors = addMessage(
|
return addMessage(
|
||||||
"Unable to create binding for %s."
|
"Unable to create binding for %s."
|
||||||
+ " It was already configured on one or more child injectors or private modules"
|
+ " It was already configured on one or more child injectors or private modules"
|
||||||
+ "%s%n"
|
+ "%s%n"
|
||||||
+ " If it was in a PrivateModule, did you forget to expose the binding?",
|
+ " If it was in a PrivateModule, did you forget to expose the binding?",
|
||||||
key, allSources.out());
|
key, allSources.out());
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
|
public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
|
||||||
return addMessage(
|
return addMessage(
|
||||||
"A binding to %s was already configured at %s and an error was thrown "
|
"A binding to %s was already configured at %s and an error was thrown "
|
||||||
+ "while checking duplicate bindings. Error: %s",
|
+ "while checking duplicate bindings. Error: %s",
|
||||||
key, convert(source), t);
|
key, Messages.convert(source), t);
|
||||||
}
|
|
||||||
|
|
||||||
public Errors errorInjectingMethod(Throwable cause) {
|
|
||||||
return errorInUserCode(cause, "Error injecting method, %s", cause);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
|
public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
|
||||||
|
@ -559,28 +406,7 @@ public final class Errors {
|
||||||
return errorInUserCode(cause,
|
return errorInUserCode(cause,
|
||||||
"Error notifying TypeListener %s (bound at %s) of %s.%n"
|
"Error notifying TypeListener %s (bound at %s) of %s.%n"
|
||||||
+ " Reason: %s",
|
+ " Reason: %s",
|
||||||
listener.getListener(), convert(listener.getSource()), type, cause);
|
listener.getListener(), Messages.convert(listener.getSource()), type, cause);
|
||||||
}
|
|
||||||
|
|
||||||
public Errors errorInjectingConstructor(Throwable cause) {
|
|
||||||
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors errorInProvider(RuntimeException runtimeException) {
|
|
||||||
Throwable unwrapped = unwrap(runtimeException);
|
|
||||||
return errorInUserCode(unwrapped, "Error in custom provider, %s", unwrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors errorInUserInjector(
|
|
||||||
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
|
|
||||||
return errorInUserCode(cause, "Error injecting %s using %s.%n"
|
|
||||||
+ " Reason: %s", type, listener, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors errorNotifyingInjectionListener(
|
|
||||||
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
|
|
||||||
return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
|
|
||||||
+ " Reason: %s", listener, type, cause);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors exposedButNotBound(Key<?> key) {
|
public Errors exposedButNotBound(Key<?> key) {
|
||||||
|
@ -595,6 +421,18 @@ public final class Errors {
|
||||||
return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
|
return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
|
||||||
|
if (throwable instanceof ProvisionException) {
|
||||||
|
return ((ProvisionException) throwable).getErrorMessages();
|
||||||
|
} else if (throwable instanceof ConfigurationException) {
|
||||||
|
return ((ConfigurationException) throwable).getErrorMessages();
|
||||||
|
} else if (throwable instanceof CreationException) {
|
||||||
|
return ((CreationException) throwable).getErrorMessages();
|
||||||
|
} else {
|
||||||
|
return ImmutableSet.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
|
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
|
||||||
Collection<Message> messages = getMessagesFromThrowable(cause);
|
Collection<Message> messages = getMessagesFromThrowable(cause);
|
||||||
|
|
||||||
|
@ -605,13 +443,6 @@ public final class Errors {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Throwable unwrap(RuntimeException runtimeException) {
|
|
||||||
if (runtimeException instanceof Exceptions.UnhandledCheckedUserException) {
|
|
||||||
return runtimeException.getCause();
|
|
||||||
} else {
|
|
||||||
return runtimeException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors cannotInjectRawProvider() {
|
public Errors cannotInjectRawProvider() {
|
||||||
return addMessage("Cannot inject a Provider that has no type parameter");
|
return addMessage("Cannot inject a Provider that has no type parameter");
|
||||||
|
@ -629,23 +460,10 @@ public final class Errors {
|
||||||
return addMessage("Cannot inject a TypeLiteral that has no type parameter");
|
return addMessage("Cannot inject a TypeLiteral that has no type parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Errors cannotSatisfyCircularDependency(Class<?> expectedType) {
|
|
||||||
return addMessage(
|
|
||||||
"Tried proxying %s to support a circular dependency, but it is not an interface.",
|
|
||||||
expectedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors circularProxiesDisabled(Class<?> expectedType) {
|
|
||||||
return addMessage(
|
|
||||||
"Tried proxying %s to support a circular dependency, but circular proxies are disabled.",
|
|
||||||
expectedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void throwCreationExceptionIfErrorsExist() {
|
public void throwCreationExceptionIfErrorsExist() {
|
||||||
if (!hasErrors()) {
|
if (!hasErrors()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CreationException(getMessages());
|
throw new CreationException(getMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,28 +471,13 @@ public final class Errors {
|
||||||
if (!hasErrors()) {
|
if (!hasErrors()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ConfigurationException(getMessages());
|
throw new ConfigurationException(getMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void throwProvisionExceptionIfErrorsExist() {
|
|
||||||
if (!hasErrors()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ProvisionException(getMessages());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Message merge(Message message) {
|
|
||||||
List<Object> sources = Lists.newArrayList();
|
|
||||||
sources.addAll(getSources());
|
|
||||||
sources.addAll(message.getSources());
|
|
||||||
return new Message(sources, message.getMessage(), message.getCause());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Errors merge(Collection<Message> messages) {
|
public Errors merge(Collection<Message> messages) {
|
||||||
|
List<Object> sources = getSources();
|
||||||
for (Message message : messages) {
|
for (Message message : messages) {
|
||||||
addMessage(merge(message));
|
addMessage(Messages.mergeSources(sources, message));
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -683,11 +486,15 @@ public final class Errors {
|
||||||
if (moreErrors.root == root || moreErrors.root.errors == null) {
|
if (moreErrors.root == root || moreErrors.root.errors == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
merge(moreErrors.root.errors);
|
merge(moreErrors.root.errors);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Errors merge(InternalProvisionException ipe) {
|
||||||
|
merge(ipe.getErrors());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Object> getSources() {
|
public List<Object> getSources() {
|
||||||
List<Object> sources = Lists.newArrayList();
|
List<Object> sources = Lists.newArrayList();
|
||||||
for (Errors e = this; e != null; e = e.parent) {
|
for (Errors e = this; e != null; e = e.parent) {
|
||||||
|
@ -719,8 +526,7 @@ public final class Errors {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
|
private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
|
||||||
String message = format(messageFormat, arguments);
|
addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
|
||||||
addMessage(new Message(getSources(), message, cause));
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,7 +542,6 @@ public final class Errors {
|
||||||
if (root.errors == null) {
|
if (root.errors == null) {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Ordering<Message>() {
|
return new Ordering<Message>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(Message a, Message b) {
|
public int compare(Message a, Message b) {
|
||||||
|
@ -745,73 +550,7 @@ public final class Errors {
|
||||||
}.sortedCopy(root.errors);
|
}.sortedCopy(root.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns {@code value} if it is non-null allowed to be null. Otherwise a message is added and
|
|
||||||
* an {@code ErrorsException} is thrown.
|
|
||||||
*/
|
|
||||||
public <T> T checkForNull(T value, Object source, Dependency<?> dependency)
|
|
||||||
throws ErrorsException {
|
|
||||||
if (value != null || dependency.isNullable()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
|
|
||||||
if (dependency.getInjectionPoint().getMember() instanceof Method) {
|
|
||||||
Method annotated = (Method) dependency.getInjectionPoint().getMember();
|
|
||||||
if (annotated.isAnnotationPresent(Provides.class)) {
|
|
||||||
switch (InternalFlags.getNullableProvidesOption()) {
|
|
||||||
case ERROR:
|
|
||||||
break; // break out & let the below exception happen
|
|
||||||
case IGNORE:
|
|
||||||
return value; // user doesn't care about injecting nulls to non-@Nullables.
|
|
||||||
case WARN:
|
|
||||||
// Warn only once, otherwise we spam logs too much.
|
|
||||||
if (!warnedDependencies.add(dependency)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
/*logger.log(Level.WARNING,
|
|
||||||
"Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
|
|
||||||
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
|
|
||||||
+ " error.",
|
|
||||||
new Object[]{
|
|
||||||
dependency.getParameterIndex(),
|
|
||||||
convert(dependency.getInjectionPoint().getMember()),
|
|
||||||
convert(dependency.getKey())});*/
|
|
||||||
return null; // log & exit.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int parameterIndex = dependency.getParameterIndex();
|
|
||||||
String parameterName = (parameterIndex != -1)
|
|
||||||
? "parameter " + parameterIndex + " of "
|
|
||||||
: "";
|
|
||||||
addMessage("null returned by binding at %s%n but %s%s is not @Nullable",
|
|
||||||
source, parameterName, dependency.getInjectionPoint().getMember());
|
|
||||||
|
|
||||||
throw toException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return root.errors == null ? 0 : root.errors.size();
|
return root.errors == null ? 0 : root.errors.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static abstract class Converter<T> {
|
|
||||||
|
|
||||||
final Class<T> type;
|
|
||||||
|
|
||||||
Converter(Class<T> type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean appliesTo(Object o) {
|
|
||||||
return o != null && type.isAssignableFrom(o.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
String convert(Object o) {
|
|
||||||
return toString(type.cast(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract String toString(T t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ package com.google.inject.internal;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class ErrorsException extends Exception {
|
public class ErrorsException extends Exception {
|
||||||
|
|
||||||
|
// NOTE: this is used by Gin which is abandoned. So changing this API will prevent Gin users from
|
||||||
|
// upgrading Guice version.
|
||||||
|
|
||||||
private final Errors errors;
|
private final Errors errors;
|
||||||
|
|
||||||
public ErrorsException(Errors errors) {
|
public ErrorsException(Errors errors) {
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
package com.google.inject.internal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rethrows user-code exceptions in wrapped exceptions so that Errors can target the correct
|
|
||||||
* exception.
|
|
||||||
*/
|
|
||||||
class Exceptions {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rethrows the exception (or it's cause, if it has one) directly if possible.
|
|
||||||
* If it was a checked exception, this wraps the exception in a stack trace
|
|
||||||
* with no frames, so that the exception is shown immediately with no frames
|
|
||||||
* above it.
|
|
||||||
*/
|
|
||||||
public static RuntimeException rethrowCause(Throwable throwable) {
|
|
||||||
Throwable cause = throwable;
|
|
||||||
if (cause.getCause() != null) {
|
|
||||||
cause = cause.getCause();
|
|
||||||
}
|
|
||||||
return rethrow(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rethrows the exception.
|
|
||||||
*/
|
|
||||||
public static RuntimeException rethrow(Throwable throwable) {
|
|
||||||
if (throwable instanceof RuntimeException) {
|
|
||||||
throw (RuntimeException) throwable;
|
|
||||||
} else if (throwable instanceof Error) {
|
|
||||||
throw (Error) throwable;
|
|
||||||
} else {
|
|
||||||
throw new UnhandledCheckedUserException(throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A marker exception class that we look for in order to unwrap the exception
|
|
||||||
* into the user exception, to provide a cleaner stack trace.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
static class UnhandledCheckedUserException extends RuntimeException {
|
|
||||||
public UnhandledCheckedUserException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,14 +22,17 @@ public final class ExposedBindingImpl<T> extends BindingImpl<T> implements Expos
|
||||||
this.privateElements = privateElements;
|
this.privateElements = privateElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PrivateElements getPrivateElements() {
|
public PrivateElements getPrivateElements() {
|
||||||
return privateElements;
|
return privateElements;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
||||||
this.privateElements = privateElements;
|
this.privateElements = privateElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notify(Errors errors) {
|
public void notify(Errors errors) {
|
||||||
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
||||||
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
|
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
|
||||||
|
@ -33,8 +34,8 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
||||||
this.delegate = explicitBinding;
|
this.delegate = explicitBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
return delegate.getInternalFactory().get(errors, context, dependency, linked);
|
return delegate.getInternalFactory().get(context, dependency, linked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,14 @@ public class ExposureBuilder<T> implements AnnotatedElementBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void annotatedWith(Class<? extends Annotation> annotationType) {
|
public void annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
Preconditions.checkNotNull(annotationType, "annotationType");
|
Preconditions.checkNotNull(annotationType, "annotationType");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
key = Key.get(key.getTypeLiteral(), annotationType);
|
key = Key.get(key.getTypeLiteral(), annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void annotatedWith(Annotation annotation) {
|
public void annotatedWith(Annotation annotation) {
|
||||||
Preconditions.checkNotNull(annotation, "annotation");
|
Preconditions.checkNotNull(annotation, "annotation");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
|
|
|
@ -25,6 +25,7 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notify(final Errors errors) {
|
public void notify(final Errors errors) {
|
||||||
try {
|
try {
|
||||||
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
||||||
|
@ -33,11 +34,15 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
context.pushState(targetKey, source);
|
throws InternalProvisionException {
|
||||||
|
Key<? extends T> localTargetKey = targetKey;
|
||||||
|
context.pushState(localTargetKey, source);
|
||||||
try {
|
try {
|
||||||
return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
|
return targetFactory.get(context, dependency, true);
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(localTargetKey);
|
||||||
} finally {
|
} finally {
|
||||||
context.popState();
|
context.popState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ package com.google.inject.internal;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
|
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
|
||||||
|
@ -10,8 +13,8 @@ import com.google.common.cache.LoadingCache;
|
||||||
*/
|
*/
|
||||||
public abstract class FailableCache<K, V> {
|
public abstract class FailableCache<K, V> {
|
||||||
|
|
||||||
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
|
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder()
|
||||||
new CacheLoader<K, Object>() {
|
.build(new CacheLoader<>() {
|
||||||
public Object load(K key) {
|
public Object load(K key) {
|
||||||
Errors errors = new Errors();
|
Errors errors = new Errors();
|
||||||
V result = null;
|
V result = null;
|
||||||
|
@ -41,4 +44,14 @@ public abstract class FailableCache<K, V> {
|
||||||
boolean remove(K key) {
|
boolean remove(K key) {
|
||||||
return delegate.asMap().remove(key) != null;
|
return delegate.asMap().remove(key) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<K, V> asMap() {
|
||||||
|
return Maps.transformValues(Maps.filterValues(ImmutableMap.copyOf(delegate.asMap()),
|
||||||
|
resultOrError -> !(resultOrError instanceof Errors)),
|
||||||
|
resultOrError -> {
|
||||||
|
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
|
||||||
|
V result = (V) resultOrError;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.google.inject.multibindings;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
|
@ -23,17 +23,22 @@ import java.lang.annotation.Annotation;
|
||||||
/**
|
/**
|
||||||
* Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
|
* Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
|
||||||
* deduplication that Guice internally performs.
|
* deduplication that Guice internally performs.
|
||||||
|
*
|
||||||
|
* <p>Note: simply using equals/hashCode on the BindingImpls doesn't work because they all have
|
||||||
|
* unique annotations. This works around that by reimplementing equality semantics that ignores
|
||||||
|
* {@link Element#uniqueId()}. A better solution might be to introduce the idea of an 'anonymous'
|
||||||
|
* binding to guice, that might support this usecase directly.
|
||||||
*/
|
*/
|
||||||
class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
|
public class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
|
||||||
implements BindingScopingVisitor<Object> {
|
implements BindingScopingVisitor<Object> {
|
||||||
private static final Object EAGER_SINGLETON = new Object();
|
private static final Object EAGER_SINGLETON = new Object();
|
||||||
final Injector injector;
|
private final Injector injector;
|
||||||
|
|
||||||
Indexer(Injector injector) {
|
public Indexer(Injector injector) {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isIndexable(Binding<?> binding) {
|
public boolean isIndexable(Binding<?> binding) {
|
||||||
return binding.getKey().getAnnotation() instanceof Element;
|
return binding.getKey().getAnnotation() instanceof Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +131,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
||||||
PROVIDED_BY,
|
PROVIDED_BY,
|
||||||
}
|
}
|
||||||
|
|
||||||
static class IndexedBinding {
|
public static class IndexedBinding {
|
||||||
final String annotationName;
|
final String annotationName;
|
||||||
final Element.Type annotationType;
|
final Element.Type annotationType;
|
||||||
final TypeLiteral<?> typeLiteral;
|
final TypeLiteral<?> typeLiteral;
|
||||||
|
@ -134,7 +139,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
||||||
final BindingType type;
|
final BindingType type;
|
||||||
final Object extraEquality;
|
final Object extraEquality;
|
||||||
|
|
||||||
IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
|
public IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.extraEquality = extraEquality;
|
this.extraEquality = extraEquality;
|
|
@ -3,17 +3,23 @@ package com.google.inject.internal;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
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.Sets;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Scope;
|
import com.google.inject.Scope;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.InjectionRequest;
|
||||||
|
import com.google.inject.spi.MembersInjectorLookup;
|
||||||
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||||
|
import com.google.inject.spi.ProviderLookup;
|
||||||
import com.google.inject.spi.ProvisionListenerBinding;
|
import com.google.inject.spi.ProvisionListenerBinding;
|
||||||
import com.google.inject.spi.ScopeBinding;
|
import com.google.inject.spi.ScopeBinding;
|
||||||
|
import com.google.inject.spi.StaticInjectionRequest;
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
import com.google.inject.spi.TypeListenerBinding;
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -27,9 +33,13 @@ final class InheritingState implements State {
|
||||||
|
|
||||||
// Must be a linked hashmap in order to preserve order of bindings in Modules.
|
// Must be a linked hashmap in order to preserve order of bindings in Modules.
|
||||||
private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
|
private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
|
||||||
private final Map<Key<?>, Binding<?>> explicitBindings
|
private final Map<Key<?>, Binding<?>> explicitBindings =
|
||||||
= Collections.unmodifiableMap(explicitBindingsMutable);
|
Collections.unmodifiableMap(explicitBindingsMutable);
|
||||||
private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
|
private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
|
||||||
|
private final Set<ProviderLookup<?>> providerLookups = Sets.newLinkedHashSet();
|
||||||
|
private final Set<StaticInjectionRequest> staticInjectionRequests = Sets.newLinkedHashSet();
|
||||||
|
private final Set<MembersInjectorLookup<?>> membersInjectorLookups = Sets.newLinkedHashSet();
|
||||||
|
private final Set<InjectionRequest<?>> injectionRequests = Sets.newLinkedHashSet();
|
||||||
private final List<TypeConverterBinding> converters = Lists.newArrayList();
|
private final List<TypeConverterBinding> converters = Lists.newArrayList();
|
||||||
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
|
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
|
||||||
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
|
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
|
||||||
|
@ -43,41 +53,95 @@ final class InheritingState implements State {
|
||||||
this.blacklistedKeys = new WeakKeySet(lock);
|
this.blacklistedKeys = new WeakKeySet(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public State parent() {
|
public State parent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
|
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
|
||||||
|
@Override
|
||||||
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
|
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
|
||||||
Binding<?> binding = explicitBindings.get(key);
|
Binding<?> binding = explicitBindings.get(key);
|
||||||
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
|
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
|
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
|
||||||
return explicitBindings;
|
return explicitBindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void putBinding(Key<?> key, BindingImpl<?> binding) {
|
public void putBinding(Key<?> key, BindingImpl<?> binding) {
|
||||||
explicitBindingsMutable.put(key, binding);
|
explicitBindingsMutable.put(key, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putProviderLookup(ProviderLookup<?> lookup) {
|
||||||
|
providerLookups.add(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ProviderLookup<?>> getProviderLookupsThisLevel() {
|
||||||
|
return providerLookups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
|
||||||
|
staticInjectionRequests.add(staticInjectionRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<StaticInjectionRequest> getStaticInjectionRequestsThisLevel() {
|
||||||
|
return staticInjectionRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putInjectionRequest(InjectionRequest<?> injectionRequest) {
|
||||||
|
injectionRequests.add(injectionRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<InjectionRequest<?>> getInjectionRequestsThisLevel() {
|
||||||
|
return injectionRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putMembersInjectorLookup(MembersInjectorLookup<?> membersInjectorLookup) {
|
||||||
|
membersInjectorLookups.add(membersInjectorLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<MembersInjectorLookup<?>> getMembersInjectorLookupsThisLevel() {
|
||||||
|
return membersInjectorLookups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
|
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
|
||||||
ScopeBinding scopeBinding = scopes.get(annotationType);
|
ScopeBinding scopeBinding = scopes.get(annotationType);
|
||||||
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
|
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
|
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
|
||||||
scopes.put(annotationType, scope);
|
scopes.put(annotationType, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ScopeBinding> getScopeBindingsThisLevel() {
|
||||||
|
return scopes.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
|
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
|
||||||
return converters;
|
return converters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addConverter(TypeConverterBinding typeConverterBinding) {
|
public void addConverter(TypeConverterBinding typeConverterBinding) {
|
||||||
converters.add(typeConverterBinding);
|
converters.add(typeConverterBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TypeConverterBinding getConverter(
|
public TypeConverterBinding getConverter(
|
||||||
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
|
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
|
||||||
TypeConverterBinding matchingConverter = null;
|
TypeConverterBinding matchingConverter = null;
|
||||||
|
@ -94,10 +158,12 @@ final class InheritingState implements State {
|
||||||
return matchingConverter;
|
return matchingConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addTypeListener(TypeListenerBinding listenerBinding) {
|
public void addTypeListener(TypeListenerBinding listenerBinding) {
|
||||||
typeListenerBindings.add(listenerBinding);
|
typeListenerBindings.add(listenerBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<TypeListenerBinding> getTypeListenerBindings() {
|
public List<TypeListenerBinding> getTypeListenerBindings() {
|
||||||
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
|
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
|
||||||
List<TypeListenerBinding> result =
|
List<TypeListenerBinding> result =
|
||||||
|
@ -107,10 +173,17 @@ final class InheritingState implements State {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TypeListenerBinding> getTypeListenerBindingsThisLevel() {
|
||||||
|
return typeListenerBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
|
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
|
||||||
provisionListenerBindings.add(listenerBinding);
|
provisionListenerBindings.add(listenerBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
|
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
|
||||||
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
|
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
|
||||||
List<ProvisionListenerBinding> result =
|
List<ProvisionListenerBinding> result =
|
||||||
|
@ -120,10 +193,17 @@ final class InheritingState implements State {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProvisionListenerBinding> getProvisionListenerBindingsThisLevel() {
|
||||||
|
return provisionListenerBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
|
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
|
||||||
scannerBindings.add(scanner);
|
scannerBindings.add(scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
|
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
|
||||||
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
|
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
|
||||||
List<ModuleAnnotatedMethodScannerBinding> result =
|
List<ModuleAnnotatedMethodScannerBinding> result =
|
||||||
|
@ -133,23 +213,33 @@ final class InheritingState implements State {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindingsThisLevel() {
|
||||||
|
return scannerBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void blacklist(Key<?> key, State state, Object source) {
|
public void blacklist(Key<?> key, State state, Object source) {
|
||||||
parent.blacklist(key, state, source);
|
parent.blacklist(key, state, source);
|
||||||
blacklistedKeys.add(key, state, source);
|
blacklistedKeys.add(key, state, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isBlacklisted(Key<?> key) {
|
public boolean isBlacklisted(Key<?> key) {
|
||||||
return blacklistedKeys.contains(key);
|
return blacklistedKeys.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
|
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
|
||||||
return blacklistedKeys.getSources(key);
|
return blacklistedKeys.getSources(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object lock() {
|
public Object lock() {
|
||||||
return lock;
|
return lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Class<? extends Annotation>, Scope> getScopes() {
|
public Map<Class<? extends Annotation>, Scope> getScopes() {
|
||||||
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
|
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
|
||||||
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {
|
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {
|
||||||
|
|
|
@ -8,5 +8,5 @@ interface Initializable<T> {
|
||||||
/**
|
/**
|
||||||
* Ensures the reference is initialized, then returns it.
|
* Ensures the reference is initialized, then returns it.
|
||||||
*/
|
*/
|
||||||
T get(Errors errors) throws ErrorsException;
|
T get() throws InternalProvisionException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ final class Initializables {
|
||||||
* Returns an initializable for an instance that requires no initialization.
|
* Returns an initializable for an instance that requires no initialization.
|
||||||
*/
|
*/
|
||||||
static <T> Initializable<T> of(final T instance) {
|
static <T> Initializable<T> of(final T instance) {
|
||||||
return new Initializable<T>() {
|
return new Initializable<>() {
|
||||||
public T get(Errors errors) throws ErrorsException {
|
public T get() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,16 @@ package com.google.inject.internal;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
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.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Stage;
|
import com.google.inject.Stage;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
@ -23,27 +24,30 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
*/
|
*/
|
||||||
final class Initializer {
|
final class Initializer {
|
||||||
|
|
||||||
/**
|
/** Is set to true once {@link #validateOustandingInjections} is called. */
|
||||||
* the only thread that we'll use to inject members.
|
private volatile boolean validationStarted = false;
|
||||||
*/
|
|
||||||
private final Thread creatingThread = Thread.currentThread();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zero means everything is injected.
|
* Allows us to detect circular dependencies. It's only used during injectable reference
|
||||||
|
* initialization. After initialization direct access through volatile field is used.
|
||||||
*/
|
*/
|
||||||
private final CountDownLatch ready = new CountDownLatch(1);
|
private final CycleDetectingLock.CycleDetectingLockFactory<Class<?>> cycleDetectingLockFactory =
|
||||||
|
new CycleDetectingLock.CycleDetectingLockFactory<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps from instances that need injection to the MembersInjector that will inject them.
|
* Instances that need injection during injector creation to a source that registered them. New
|
||||||
|
* references added before {@link #validateOustandingInjections}. Cleared up in {@link
|
||||||
|
* #injectAll}.
|
||||||
*/
|
*/
|
||||||
private final Map<Object, MembersInjectorImpl<?>> pendingMembersInjectors =
|
private final List<InjectableReference<?>> pendingInjections = Lists.newArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map that guarantees that no instance would get two references. New references added before
|
||||||
|
* {@link #validateOustandingInjections}. Cleared up in {@link #validateOustandingInjections}.
|
||||||
|
*/
|
||||||
|
private final IdentityHashMap<Object, InjectableReference<?>> initializablesCache =
|
||||||
Maps.newIdentityHashMap();
|
Maps.newIdentityHashMap();
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps instances that need injection to a source that registered them
|
|
||||||
*/
|
|
||||||
private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an instance for member injection when that step is performed.
|
* Registers an instance for member injection when that step is performed.
|
||||||
*
|
*
|
||||||
|
@ -55,21 +59,35 @@ final class Initializer {
|
||||||
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
|
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
|
||||||
Object source, Set<InjectionPoint> injectionPoints) {
|
Object source, Set<InjectionPoint> injectionPoints) {
|
||||||
checkNotNull(source);
|
checkNotNull(source);
|
||||||
|
Preconditions.checkState(
|
||||||
|
!validationStarted, "Member injection could not be requested after validation is started");
|
||||||
ProvisionListenerStackCallback<T> provisionCallback =
|
ProvisionListenerStackCallback<T> provisionCallback =
|
||||||
binding == null ? null : injector.provisionListenerStore.get(binding);
|
binding == null ? null : injector.provisionListenerStore.get(binding);
|
||||||
|
|
||||||
// short circuit if the object has no injections or listeners.
|
// short circuit if the object has no injections or listeners.
|
||||||
if (instance == null || (injectionPoints.isEmpty()
|
if (instance == null || (injectionPoints.isEmpty()
|
||||||
&& !injector.membersInjectorStore.hasTypeListeners()
|
&& !injector.membersInjectorStore.hasTypeListeners()
|
||||||
&& (provisionCallback == null || !provisionCallback.hasListeners()))) {
|
&& (provisionCallback == null))) {
|
||||||
return Initializables.of(instance);
|
return Initializables.of(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
InjectableReference<T> initializable = new InjectableReference<T>(
|
if (initializablesCache.containsKey(instance)) {
|
||||||
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
|
@SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
|
||||||
pendingInjection.put(instance, initializable);
|
Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
|
||||||
return initializable;
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
InjectableReference<T> injectableReference =
|
||||||
|
new InjectableReference<T>(
|
||||||
|
injector,
|
||||||
|
instance,
|
||||||
|
binding == null ? null : binding.getKey(),
|
||||||
|
provisionCallback,
|
||||||
|
source,
|
||||||
|
cycleDetectingLockFactory.create(instance.getClass()));
|
||||||
|
initializablesCache.put(instance, injectableReference);
|
||||||
|
pendingInjections.add(injectableReference);
|
||||||
|
return injectableReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,9 +95,11 @@ final class Initializer {
|
||||||
* on the injected instances.
|
* on the injected instances.
|
||||||
*/
|
*/
|
||||||
void validateOustandingInjections(Errors errors) {
|
void validateOustandingInjections(Errors errors) {
|
||||||
for (InjectableReference<?> reference : pendingInjection.values()) {
|
validationStarted = true;
|
||||||
|
initializablesCache.clear();
|
||||||
|
for (InjectableReference<?> reference : pendingInjections) {
|
||||||
try {
|
try {
|
||||||
pendingMembersInjectors.put(reference.instance, reference.validate(errors));
|
reference.validate(errors);
|
||||||
} catch (ErrorsException e) {
|
} catch (ErrorsException e) {
|
||||||
errors.merge(e.getErrors());
|
errors.merge(e.getErrors());
|
||||||
}
|
}
|
||||||
|
@ -92,85 +112,122 @@ final class Initializer {
|
||||||
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
||||||
*/
|
*/
|
||||||
void injectAll(final Errors errors) {
|
void injectAll(final Errors errors) {
|
||||||
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
|
Preconditions.checkState(validationStarted, "Validation should be done before injection");
|
||||||
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
|
for (InjectableReference<?> reference : pendingInjections) {
|
||||||
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
|
|
||||||
try {
|
try {
|
||||||
reference.get(errors);
|
reference.get();
|
||||||
} catch (ErrorsException e) {
|
} catch (InternalProvisionException ipe) {
|
||||||
errors.merge(e.getErrors());
|
errors.merge(ipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pendingInjections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (!pendingInjection.isEmpty()) {
|
private enum InjectableReferenceState {
|
||||||
throw new AssertionError("Failed to satisfy " + pendingInjection);
|
NEW,
|
||||||
|
VALIDATED,
|
||||||
|
INJECTING,
|
||||||
|
READY
|
||||||
}
|
}
|
||||||
|
|
||||||
ready.countDown();
|
private static class InjectableReference<T> implements Initializable<T> {
|
||||||
}
|
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
|
||||||
|
private volatile MembersInjectorImpl<T> membersInjector = null;
|
||||||
|
|
||||||
private class InjectableReference<T> implements Initializable<T> {
|
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
private final T instance;
|
private final T instance;
|
||||||
private final Object source;
|
private final Object source;
|
||||||
private final Key<T> key;
|
private final Key<T> key;
|
||||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
private final CycleDetectingLock<?> lock;
|
||||||
|
|
||||||
public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
|
public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
|
||||||
ProvisionListenerStackCallback<T> provisionCallback, Object source) {
|
ProvisionListenerStackCallback<T> provisionCallback,
|
||||||
|
Object source,
|
||||||
|
CycleDetectingLock<?> lock) {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.key = key; // possibly null!
|
this.key = key; // possibly null!
|
||||||
this.provisionCallback = provisionCallback; // possibly null!
|
this.provisionCallback = provisionCallback; // possibly null!
|
||||||
this.instance = checkNotNull(instance, "instance");
|
this.instance = checkNotNull(instance, "instance");
|
||||||
this.source = checkNotNull(source, "source");
|
this.source = checkNotNull(source, "source");
|
||||||
|
this.lock = checkNotNull(lock, "lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
public MembersInjectorImpl<T> validate(Errors errors) throws ErrorsException {
|
public void validate(Errors errors) throws ErrorsException {
|
||||||
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
|
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
|
||||||
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||||
return injector.membersInjectorStore.get(type, errors.withSource(source));
|
membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
|
||||||
|
Preconditions.checkNotNull(
|
||||||
|
membersInjector,
|
||||||
|
"No membersInjector available for instance: %s, from key: %s",
|
||||||
|
instance,
|
||||||
|
key);
|
||||||
|
state = InjectableReferenceState.VALIDATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this
|
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this
|
||||||
* method will ensure that all its members have been injected before returning.
|
* method will ensure that all its members have been injected before returning.
|
||||||
*/
|
*/
|
||||||
public T get(Errors errors) throws ErrorsException {
|
@Override
|
||||||
if (ready.getCount() == 0) {
|
public T get() throws InternalProvisionException {
|
||||||
|
// skipping acquiring lock if initialization is already finished
|
||||||
|
if (state == InjectableReferenceState.READY) {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
// acquire lock for current binding to initialize an instance
|
||||||
|
Multimap<?, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
|
||||||
|
if (!lockCycle.isEmpty()) {
|
||||||
|
// Potential deadlock detected and creation lock is not taken.
|
||||||
|
// According to injectAll()'s contract return non-initialized instance.
|
||||||
|
|
||||||
// just wait for everything to be injected by another thread
|
// This condition should not be possible under the current Guice implementation.
|
||||||
if (Thread.currentThread() != creatingThread) {
|
// This clause exists for defensive programming purposes.
|
||||||
|
|
||||||
|
// Reasoning:
|
||||||
|
// get() is called either directly from injectAll(), holds no locks and can not create
|
||||||
|
// a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
|
||||||
|
// Before calling get() object has to be requested for injection.
|
||||||
|
// Initializer.requestInjection() is called either for constant object bindings, which wrap
|
||||||
|
// creation into a Singleton scope, or from Binder.requestInjection(), which
|
||||||
|
// has to use Singleton scope to reuse the same InjectableReference to potentially
|
||||||
|
// create a lock cycle.
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
ready.await();
|
// lock acquired, current thread owns this instance initialization
|
||||||
|
switch (state) {
|
||||||
|
case READY:
|
||||||
return instance;
|
return instance;
|
||||||
} catch (InterruptedException e) {
|
// When instance depends on itself in the same thread potential dead lock
|
||||||
// Give up, since we don't know if our injection is ready
|
// is not detected. We have to prevent a stack overflow and we use
|
||||||
throw new RuntimeException(e);
|
// an "injecting" stage to short-circuit a call.
|
||||||
|
case INJECTING:
|
||||||
|
return instance;
|
||||||
|
case VALIDATED:
|
||||||
|
state = InjectableReferenceState.INJECTING;
|
||||||
|
break;
|
||||||
|
case NEW:
|
||||||
|
throw new IllegalStateException("InjectableReference is not validated yet");
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown state: " + state);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// toInject needs injection, do it right away. we only do this once, even if it fails
|
|
||||||
if (pendingInjection.remove(instance) != null) {
|
|
||||||
// safe because we only insert a members injector for the appropriate instance
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
MembersInjectorImpl<T> membersInjector =
|
|
||||||
(MembersInjectorImpl<T>) pendingMembersInjectors.remove(instance);
|
|
||||||
Preconditions.checkState(membersInjector != null,
|
|
||||||
"No membersInjector available for instance: %s, from key: %s", instance, key);
|
|
||||||
|
|
||||||
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
||||||
// (otherwise we'll inject all of them)
|
// (otherwise we'll inject all of them)
|
||||||
membersInjector.injectAndNotify(instance,
|
try {
|
||||||
errors.withSource(source),
|
membersInjector.injectAndNotify(
|
||||||
key,
|
instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
|
||||||
provisionCallback,
|
} catch (InternalProvisionException ipe) {
|
||||||
source,
|
throw ipe.addSource(source);
|
||||||
injector.options.stage == Stage.TOOL);
|
|
||||||
}
|
}
|
||||||
|
// mark instance as ready to skip a lock on subsequent calls
|
||||||
|
state = InjectableReferenceState.READY;
|
||||||
return instance;
|
return instance;
|
||||||
|
} finally {
|
||||||
|
// always release our creation lock, even on failures
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -98,21 +98,19 @@ final class InjectionRequestProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void injectMembers() {
|
void injectMembers() {
|
||||||
try {
|
try (InternalContext context = injector.enterContext()) {
|
||||||
injector.callInContext(new ContextualCallable<Void>() {
|
boolean isStageTool = injector.options.stage == Stage.TOOL;
|
||||||
public Void call(InternalContext context) {
|
|
||||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||||
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
|
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
|
||||||
// or if we are in tool stage and the injection point is toolable.
|
// or if we are in tool stage and the injection point is toolable.
|
||||||
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
|
if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
|
||||||
memberInjector.inject(errors, context, null);
|
try {
|
||||||
|
memberInjector.inject(context, null);
|
||||||
|
} catch (InternalProvisionException e) {
|
||||||
|
errors.merge(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} catch (ErrorsException e) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
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.Lists;
|
import com.google.common.collect.ListMultimap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Multimaps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
|
@ -18,7 +22,6 @@ import com.google.inject.MembersInjector;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.google.inject.ProvidedBy;
|
import com.google.inject.ProvidedBy;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.ProvisionException;
|
|
||||||
import com.google.inject.Scope;
|
import com.google.inject.Scope;
|
||||||
import com.google.inject.Stage;
|
import com.google.inject.Stage;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
@ -26,8 +29,10 @@ import com.google.inject.internal.util.SourceProvider;
|
||||||
import com.google.inject.spi.BindingTargetVisitor;
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
import com.google.inject.spi.ConvertedConstantBinding;
|
import com.google.inject.spi.ConvertedConstantBinding;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
import com.google.inject.spi.HasDependencies;
|
import com.google.inject.spi.HasDependencies;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.InstanceBinding;
|
||||||
import com.google.inject.spi.ProviderBinding;
|
import com.google.inject.spi.ProviderBinding;
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
import com.google.inject.util.Providers;
|
import com.google.inject.util.Providers;
|
||||||
|
@ -42,45 +47,46 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default {@link Injector} implementation.
|
* Default {@link Injector} implementation.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
final class InjectorImpl implements Injector, Lookups {
|
final class InjectorImpl implements Injector, Lookups {
|
||||||
|
|
||||||
public static final TypeLiteral<String> STRING_TYPE = TypeLiteral.get(String.class);
|
public static final TypeLiteral<String> STRING_TYPE = TypeLiteral.get(String.class);
|
||||||
/**
|
|
||||||
* Synchronization: map value is modified only for the current thread,
|
|
||||||
* it's ok to read map values of other threads. It can change between your
|
|
||||||
* calls.
|
|
||||||
*
|
|
||||||
* @see #getGlobalInternalContext
|
|
||||||
*/
|
|
||||||
private static final ConcurrentMap<Thread, InternalContext> globalInternalContext =
|
|
||||||
Maps.newConcurrentMap();
|
|
||||||
final State state;
|
final State state;
|
||||||
|
|
||||||
final InjectorImpl parent;
|
final InjectorImpl parent;
|
||||||
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
|
|
||||||
|
final ListMultimap<TypeLiteral<?>, Binding<?>> bindingsMultimap = ArrayListMultimap.create();
|
||||||
|
|
||||||
final InjectorOptions options;
|
final InjectorOptions options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just-in-time binding cache. Guarded by state.lock()
|
* Just-in-time binding cache. Guarded by state.lock()
|
||||||
*/
|
*/
|
||||||
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
|
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of Keys that we were unable to create JIT bindings for, so we don't
|
* Cache of Keys that we were unable to create JIT bindings for, so we don't
|
||||||
* keep trying. Also guarded by state.lock().
|
* keep trying. Also guarded by state.lock().
|
||||||
*/
|
*/
|
||||||
final Set<Key<?>> failedJitBindings = Sets.newHashSet();
|
final Set<Key<?>> failedJitBindings = Sets.newHashSet();
|
||||||
|
|
||||||
|
Lookups lookups = new DeferredLookups(this);
|
||||||
|
|
||||||
|
/** The set of types passed to {@link #getMembersInjector} and {@link #injectMembers}. */
|
||||||
|
final Set<TypeLiteral<?>> userRequestedMembersInjectorTypes = Sets.newConcurrentHashSet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached constructor injectors for each type
|
* Cached constructor injectors for each type
|
||||||
*/
|
*/
|
||||||
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
|
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
|
||||||
/**
|
|
||||||
* @see #getGlobalInternalContext
|
|
||||||
*/
|
|
||||||
private final ThreadLocal<Object[]> localContext;
|
private final ThreadLocal<Object[]> localContext;
|
||||||
Lookups lookups = new DeferredLookups(this);
|
|
||||||
/**
|
/**
|
||||||
* Cached field and method injectors for each type.
|
* Cached field and method injectors for each type.
|
||||||
*/
|
*/
|
||||||
|
@ -98,56 +104,32 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
localContext = parent.localContext;
|
localContext = parent.localContext;
|
||||||
} else {
|
} else {
|
||||||
localContext = new ThreadLocal<Object[]>();
|
// No ThreadLocal.initialValue(), as that would cause classloader leaks. See
|
||||||
|
// https://github.com/google/guice/issues/288#issuecomment-48216933,
|
||||||
|
// https://github.com/google/guice/issues/288#issuecomment-48216944
|
||||||
|
localContext = new ThreadLocal<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the key type is Provider (but not a subclass of Provider).
|
/** Only to be called by the {@link SingletonScope} provider. */
|
||||||
*/
|
InternalContext getLocalContext() {
|
||||||
private static boolean isProvider(Key<?> key) {
|
return (InternalContext) localContext.get()[0];
|
||||||
return key.getTypeLiteral().getRawType().equals(Provider.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTypeLiteral(Key<?> key) {
|
InternalContext enterContext() {
|
||||||
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
|
Object[] reference = localContext.get();
|
||||||
|
if (reference == null) {
|
||||||
|
reference = new Object[1];
|
||||||
|
localContext.set(reference);
|
||||||
}
|
}
|
||||||
|
InternalContext ctx = (InternalContext) reference[0];
|
||||||
private static <T> Key<T> getProvidedKey(Key<Provider<T>> key, Errors errors) throws ErrorsException {
|
if (ctx == null) {
|
||||||
Type providerType = key.getTypeLiteral().getType();
|
reference[0] = ctx = new InternalContext(options, reference);
|
||||||
|
} else {
|
||||||
// If the Provider has no type parameter (raw Provider)...
|
ctx.enter();
|
||||||
if (!(providerType instanceof ParameterizedType)) {
|
|
||||||
throw errors.cannotInjectRawProvider().toException();
|
|
||||||
}
|
}
|
||||||
|
return ctx;
|
||||||
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>>
|
|
||||||
Key<T> providedKey = (Key<T>) key.ofType(entryType);
|
|
||||||
return providedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the key type is MembersInjector (but not a subclass of MembersInjector).
|
|
||||||
*/
|
|
||||||
private static boolean isMembersInjector(Key<?> key) {
|
|
||||||
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
|
|
||||||
&& key.getAnnotationType() == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to the internal context for the current injector of all threads.
|
|
||||||
* One does not need to use this from Guice source code as context could be passed on the stack.
|
|
||||||
* It is required for custom scopes which are called from Guice and sometimes do require
|
|
||||||
* access to current internal context, but it is not passed in. Contrary to {@link #localContext}
|
|
||||||
* it is not used to store injector-specific state, but to provide easy access to the current
|
|
||||||
* state.
|
|
||||||
*
|
|
||||||
* @return unmodifiable map
|
|
||||||
*/
|
|
||||||
static Map<Thread, InternalContext> getGlobalInternalContext() {
|
|
||||||
return Collections.unmodifiableMap(globalInternalContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,23 +137,23 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
*/
|
*/
|
||||||
void index() {
|
void index() {
|
||||||
for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) {
|
for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) {
|
||||||
index(binding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> void index(Binding<T> binding) {
|
|
||||||
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
|
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
|
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
|
||||||
return bindingsMultimap.getAll(type);
|
@SuppressWarnings("unchecked") // safe because we only put matching entries into the map
|
||||||
|
List<Binding<T>> list = (List<Binding<T>>) (List) bindingsMultimap.get(checkNotNull(type, "type"));
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the binding for {@code key}
|
* Returns the binding for {@code key}
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public <T> BindingImpl<T> getBinding(Key<T> key) {
|
public <T> BindingImpl<T> getBinding(Key<T> key) {
|
||||||
Errors errors = new Errors(key);
|
Errors errors = new Errors(checkNotNull(key, "key"));
|
||||||
try {
|
try {
|
||||||
BindingImpl<T> result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT);
|
BindingImpl<T> result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT);
|
||||||
errors.throwConfigurationExceptionIfErrorsExist();
|
errors.throwConfigurationExceptionIfErrorsExist();
|
||||||
|
@ -181,6 +163,7 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> BindingImpl<T> getExistingBinding(Key<T> key) {
|
public <T> BindingImpl<T> getExistingBinding(Key<T> key) {
|
||||||
// Check explicit bindings, i.e. bindings created by modules.
|
// Check explicit bindings, i.e. bindings created by modules.
|
||||||
BindingImpl<T> explicitBinding = state.getExplicitBinding(key);
|
BindingImpl<T> explicitBinding = state.getExplicitBinding(key);
|
||||||
|
@ -235,21 +218,22 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return getJustInTimeBinding(key, errors, jitType);
|
return getJustInTimeBinding(key, errors, jitType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Binding<T> getBinding(Class<T> type) {
|
public <T> Binding<T> getBinding(Class<T> type) {
|
||||||
return getBinding(Key.get(type));
|
return getBinding(Key.get(checkNotNull(type, "type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector getParent() {
|
public Injector getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
||||||
return new InternalInjectorCreator()
|
return new InternalInjectorCreator().parentInjector(this).addModules(modules).build();
|
||||||
.parentInjector(this)
|
|
||||||
.addModules(modules)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector createChildInjector(Module... modules) {
|
public Injector createChildInjector(Module... modules) {
|
||||||
return createChildInjector(ImmutableList.copyOf(modules));
|
return createChildInjector(ImmutableList.copyOf(modules));
|
||||||
}
|
}
|
||||||
|
@ -304,6 +288,40 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
} // end synchronized(state.lock())
|
} // end synchronized(state.lock())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the key type is Provider (but not a subclass of Provider).
|
||||||
|
*/
|
||||||
|
private static boolean isProvider(Key<?> key) {
|
||||||
|
return key.getTypeLiteral().getRawType().equals(Provider.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTypeLiteral(Key<?> key) {
|
||||||
|
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static <T> Key<T> getProvidedKey(Key<Provider<T>> key, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
|
Type providerType = key.getTypeLiteral().getType();
|
||||||
|
|
||||||
|
// If the Provider has no type parameter (raw Provider)...
|
||||||
|
if (!(providerType instanceof ParameterizedType)) {
|
||||||
|
throw errors.cannotInjectRawProvider().toException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>>
|
||||||
|
Key<T> providedKey = (Key<T>) key.ofType(entryType);
|
||||||
|
return providedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if the key type is MembersInjector (but not a subclass of MembersInjector). */
|
||||||
|
private static boolean isMembersInjector(Key<?> key) {
|
||||||
|
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
|
||||||
|
&& key.getAnnotationType() == null;
|
||||||
|
}
|
||||||
|
|
||||||
private <T> BindingImpl<MembersInjector<T>> createMembersInjectorBinding(
|
private <T> BindingImpl<MembersInjector<T>> createMembersInjectorBinding(
|
||||||
Key<MembersInjector<T>> key, Errors errors) throws ErrorsException {
|
Key<MembersInjector<T>> key, Errors errors) throws ErrorsException {
|
||||||
Type membersInjectorType = key.getTypeLiteral().getType();
|
Type membersInjectorType = key.getTypeLiteral().getType();
|
||||||
|
@ -316,12 +334,11 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]);
|
((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]);
|
||||||
MembersInjector<T> membersInjector = membersInjectorStore.get(instanceType, errors);
|
MembersInjector<T> membersInjector = membersInjectorStore.get(instanceType, errors);
|
||||||
|
|
||||||
InternalFactory<MembersInjector<T>> factory = new ConstantFactory<MembersInjector<T>>(
|
InternalFactory<MembersInjector<T>> factory =
|
||||||
Initializables.of(membersInjector));
|
new ConstantFactory<>(Initializables.of(membersInjector));
|
||||||
|
|
||||||
|
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
||||||
return new InstanceBindingImpl<MembersInjector<T>>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
factory, ImmutableSet.of(), membersInjector);
|
||||||
factory, ImmutableSet.<InjectionPoint>of(), membersInjector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -350,7 +367,11 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String stringValue = stringBinding.getProvider().get();
|
// We can't call getProvider().get() because this InstanceBinding may not have been inintialized
|
||||||
|
// yet (because we may have been called during InternalInjectorCreator.initializeStatically and
|
||||||
|
// instance binding validation hasn't happened yet.)
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
String stringValue = ((InstanceBinding<String>) stringBinding).getInstance();
|
||||||
Object source = stringBinding.getSource();
|
Object source = stringBinding.getSource();
|
||||||
|
|
||||||
// Find a matching type converter.
|
// Find a matching type converter.
|
||||||
|
@ -379,8 +400,6 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
|
|
||||||
return new ConvertedConstantBindingImpl<T>(this, key, converted, stringBinding,
|
return new ConvertedConstantBindingImpl<T>(this, key, converted, stringBinding,
|
||||||
typeConverterBinding);
|
typeConverterBinding);
|
||||||
} catch (ErrorsException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
throw errors.conversionError(stringValue, source, type, typeConverterBinding, e)
|
throw errors.conversionError(stringValue, source, type, typeConverterBinding, e)
|
||||||
.toException();
|
.toException();
|
||||||
|
@ -411,7 +430,7 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
// so that cached exceptions while constructing it get stored.
|
// so that cached exceptions while constructing it get stored.
|
||||||
// See TypeListenerTest#testTypeListenerThrows
|
// See TypeListenerTest#testTypeListenerThrows
|
||||||
removeFailedJitBinding(binding, null);
|
removeFailedJitBinding(binding, null);
|
||||||
cleanup(binding, new HashSet<Key>());
|
cleanup(binding, new HashSet<>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,18 +442,18 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
* optimistically added to allow circular dependency support, so dependencies may pass where they
|
* optimistically added to allow circular dependency support, so dependencies may pass where they
|
||||||
* should have failed.
|
* should have failed.
|
||||||
*/
|
*/
|
||||||
private boolean cleanup(BindingImpl<?> binding, Set<Key> encountered) {
|
private boolean cleanup(BindingImpl<?> binding, Set<Key<?>> encountered) {
|
||||||
boolean bindingFailed = false;
|
boolean bindingFailed = false;
|
||||||
Set<Dependency<?>> deps = getInternalDependencies(binding);
|
Set<Dependency<?>> deps = getInternalDependencies(binding);
|
||||||
for (Dependency dep : deps) {
|
for (Dependency<?> dep : deps) {
|
||||||
Key<?> depKey = dep.getKey();
|
Key<?> depKey = dep.getKey();
|
||||||
InjectionPoint ip = dep.getInjectionPoint();
|
InjectionPoint ip = dep.getInjectionPoint();
|
||||||
if (encountered.add(depKey)) { // only check if we haven't looked at this key yet
|
if (encountered.add(depKey)) { // only check if we haven't looked at this key yet
|
||||||
BindingImpl depBinding = jitBindings.get(depKey);
|
BindingImpl<?> depBinding = jitBindings.get(depKey);
|
||||||
if (depBinding != null) { // if the binding still exists, validate
|
if (depBinding != null) { // if the binding still exists, validate
|
||||||
boolean failed = cleanup(depBinding, encountered); // if children fail, we fail
|
boolean failed = cleanup(depBinding, encountered); // if children fail, we fail
|
||||||
if (depBinding instanceof ConstructorBindingImpl) {
|
if (depBinding instanceof ConstructorBindingImpl) {
|
||||||
ConstructorBindingImpl ctorBinding = (ConstructorBindingImpl) depBinding;
|
ConstructorBindingImpl<?> ctorBinding = (ConstructorBindingImpl) depBinding;
|
||||||
ip = ctorBinding.getInternalConstructor();
|
ip = ctorBinding.getInternalConstructor();
|
||||||
if (!ctorBinding.isInitialized()) {
|
if (!ctorBinding.isInitialized()) {
|
||||||
failed = true;
|
failed = true;
|
||||||
|
@ -493,7 +512,7 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
|
|
||||||
// Don't try to inject arrays or enums annotated with @ImplementedBy.
|
// Don't try to inject arrays or enums annotated with @ImplementedBy.
|
||||||
if (rawType.isArray() || (rawType.isEnum() && implementedBy != null)) {
|
if (rawType.isArray() || (rawType.isEnum() && implementedBy != null)) {
|
||||||
throw errors.missingImplementation(key).toException();
|
throw errors.missingImplementationWithHint(key, this).toException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle TypeLiteral<T> by binding the inner type
|
// Handle TypeLiteral<T> by binding the inner type
|
||||||
|
@ -552,10 +571,9 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe
|
@SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe
|
||||||
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
|
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
|
||||||
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<TypeLiteral<T>>(
|
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<>(Initializables.of(value));
|
||||||
Initializables.of(value));
|
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
||||||
return new InstanceBindingImpl<TypeLiteral<T>>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
factory, ImmutableSet.of(), value);
|
||||||
factory, ImmutableSet.<InjectionPoint>of(), value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -564,7 +582,7 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
<T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
|
<T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
|
||||||
ProvidedBy providedBy, Errors errors) throws ErrorsException {
|
ProvidedBy providedBy, Errors errors) throws ErrorsException {
|
||||||
Class<?> rawType = key.getTypeLiteral().getRawType();
|
Class<?> rawType = key.getTypeLiteral().getRawType();
|
||||||
Class<? extends Provider<?>> providerType = providedBy.value();
|
Class<? extends javax.inject.Provider<?>> providerType = providedBy.value();
|
||||||
|
|
||||||
// Make sure it's not the same type. TODO: Can we check for deeper loops?
|
// Make sure it's not the same type. TODO: Can we check for deeper loops?
|
||||||
if (providerType == rawType) {
|
if (providerType == rawType) {
|
||||||
|
@ -576,12 +594,11 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType);
|
Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType);
|
||||||
ProvidedByInternalFactory<T> internalFactory =
|
ProvidedByInternalFactory<T> internalFactory =
|
||||||
new ProvidedByInternalFactory<T>(rawType, providerType, providerKey);
|
new ProvidedByInternalFactory<T>(rawType, providerType, providerKey);
|
||||||
Object source = rawType;
|
|
||||||
BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer(
|
BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer(
|
||||||
this,
|
this,
|
||||||
key,
|
key,
|
||||||
source,
|
rawType,
|
||||||
Scoping.<T>scope(key, this, internalFactory, source, scoping),
|
Scoping.<T>scope(key, this, internalFactory, rawType, scoping),
|
||||||
scoping,
|
scoping,
|
||||||
providerKey,
|
providerKey,
|
||||||
internalFactory);
|
internalFactory);
|
||||||
|
@ -613,27 +630,14 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
|
|
||||||
// Look up the target binding.
|
// Look up the target binding.
|
||||||
final Key<? extends T> targetKey = Key.get(subclass);
|
final Key<? extends T> targetKey = Key.get(subclass);
|
||||||
final BindingImpl<? extends T> targetBinding = getBindingOrThrow(targetKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
|
|
||||||
|
|
||||||
InternalFactory<T> internalFactory = new InternalFactory<T>() {
|
FactoryProxy<T> factory = new FactoryProxy<>(this, key, targetKey, rawType);
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
factory.notify(errors); // causes the factory to initialize itself internally
|
||||||
throws ErrorsException {
|
|
||||||
context.pushState(targetKey, targetBinding.getSource());
|
|
||||||
try {
|
|
||||||
return targetBinding.getInternalFactory().get(
|
|
||||||
errors.withSource(targetKey), context, dependency, true);
|
|
||||||
} finally {
|
|
||||||
context.popState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object source = rawType;
|
|
||||||
return new LinkedBindingImpl<T>(
|
return new LinkedBindingImpl<T>(
|
||||||
this,
|
this,
|
||||||
key,
|
key,
|
||||||
source,
|
rawType,
|
||||||
Scoping.<T>scope(key, this, internalFactory, source, scoping),
|
Scoping.<T>scope(key, this, factory, rawType, scoping),
|
||||||
scoping,
|
scoping,
|
||||||
targetKey);
|
targetKey);
|
||||||
}
|
}
|
||||||
|
@ -725,9 +729,7 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return convertedBinding;
|
return convertedBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTypeLiteral(key)
|
if (!isTypeLiteral(key) && jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
|
||||||
&& jitDisabled
|
|
||||||
&& jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
|
|
||||||
throw errors.jitDisabled(key).toException();
|
throw errors.jitDisabled(key).toException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,10 +759,12 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return getBindingOrThrow(key, errors, jitType).getInternalFactory();
|
return getBindingOrThrow(key, errors, jitType).getInternalFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Key<?>, Binding<?>> getBindings() {
|
public Map<Key<?>, Binding<?>> getBindings() {
|
||||||
return state.getExplicitBindingsThisLevel();
|
return state.getExplicitBindingsThisLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Key<?>, Binding<?>> getAllBindings() {
|
public Map<Key<?>, Binding<?>> getAllBindings() {
|
||||||
synchronized (state.lock()) {
|
synchronized (state.lock()) {
|
||||||
return new ImmutableMap.Builder<Key<?>, Binding<?>>()
|
return new ImmutableMap.Builder<Key<?>, Binding<?>>()
|
||||||
|
@ -770,14 +774,47 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
||||||
return ImmutableMap.copyOf(state.getScopes());
|
return ImmutableMap.copyOf(state.getScopes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<TypeConverterBinding> getTypeConverterBindings() {
|
public Set<TypeConverterBinding> getTypeConverterBindings() {
|
||||||
return ImmutableSet.copyOf(state.getConvertersThisLevel());
|
return ImmutableSet.copyOf(state.getConvertersThisLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Element> getElements() {
|
||||||
|
ImmutableList.Builder<Element> elements = ImmutableList.builder();
|
||||||
|
elements.addAll(getAllBindings().values());
|
||||||
|
elements.addAll(state.getProviderLookupsThisLevel());
|
||||||
|
elements.addAll(state.getConvertersThisLevel());
|
||||||
|
elements.addAll(state.getScopeBindingsThisLevel());
|
||||||
|
elements.addAll(state.getTypeListenerBindingsThisLevel());
|
||||||
|
elements.addAll(state.getProvisionListenerBindingsThisLevel());
|
||||||
|
elements.addAll(state.getScannerBindingsThisLevel());
|
||||||
|
elements.addAll(state.getStaticInjectionRequestsThisLevel());
|
||||||
|
elements.addAll(state.getMembersInjectorLookupsThisLevel());
|
||||||
|
elements.addAll(state.getInjectionRequestsThisLevel());
|
||||||
|
|
||||||
|
return elements.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints() {
|
||||||
|
// Note, this is a safe cast per the ListMultimap javadocs.
|
||||||
|
// We could use Multimaps.asMap to avoid the cast, but unfortunately it's a @Beta method.
|
||||||
|
return (Map<TypeLiteral<?>, List<InjectionPoint>>)
|
||||||
|
(Map<TypeLiteral<?>, ?>)
|
||||||
|
ImmutableListMultimap.copyOf(
|
||||||
|
Multimaps.filterKeys(
|
||||||
|
membersInjectorStore.getAllInjectionPoints(),
|
||||||
|
userRequestedMembersInjectorTypes::contains))
|
||||||
|
.asMap();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns parameter injectors, or {@code null} if there are no parameters.
|
* Returns parameter injectors, or {@code null} if there are no parameters.
|
||||||
*/
|
*/
|
||||||
|
@ -808,13 +845,18 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return new SingleParameterInjector<T>(dependency, binding);
|
return new SingleParameterInjector<T>(dependency, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // the members injector type is consistent with instance's type
|
@SuppressWarnings({"unchecked","rawtypes"}) // the members injector type is consistent with instance's type
|
||||||
|
@Override
|
||||||
public void injectMembers(Object instance) {
|
public void injectMembers(Object instance) {
|
||||||
MembersInjector membersInjector = getMembersInjector(instance.getClass());
|
MembersInjector membersInjector = getMembersInjector(instance.getClass());
|
||||||
membersInjector.injectMembers(instance);
|
membersInjector.injectMembers(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||||
|
checkNotNull(typeLiteral, "typeLiteral");
|
||||||
|
userRequestedMembersInjectorTypes.add(typeLiteral);
|
||||||
|
|
||||||
Errors errors = new Errors(typeLiteral);
|
Errors errors = new Errors(typeLiteral);
|
||||||
try {
|
try {
|
||||||
return membersInjectorStore.get(typeLiteral, errors);
|
return membersInjectorStore.get(typeLiteral, errors);
|
||||||
|
@ -823,47 +865,45 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
return getMembersInjector(TypeLiteral.get(type));
|
return getMembersInjector(TypeLiteral.get(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Class<T> type) {
|
public <T> Provider<T> getProvider(Class<T> type) {
|
||||||
return getProvider(Key.get(type));
|
return getProvider(Key.get(checkNotNull(type, "type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> Provider<T> getProviderOrThrow(final Dependency<T> dependency, Errors errors) throws ErrorsException {
|
<T> Provider<T> getProviderOrThrow(final Dependency<T> dependency, Errors errors) throws ErrorsException {
|
||||||
final Key<T> key = dependency.getKey();
|
final Key<T> key = dependency.getKey();
|
||||||
final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
|
final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
|
||||||
|
final InternalFactory<? extends T> internalFactory = binding.getInternalFactory();
|
||||||
return new Provider<T>() {
|
final Object source = binding.getSource();
|
||||||
|
return new Provider<>() {
|
||||||
public T get() {
|
public T get() {
|
||||||
final Errors errors = new Errors(dependency);
|
InternalContext currentContext = enterContext();
|
||||||
|
Dependency<?> previous = currentContext.pushDependency(dependency, source);
|
||||||
try {
|
try {
|
||||||
T t = callInContext(new ContextualCallable<T>() {
|
return internalFactory.get(currentContext, dependency, false);
|
||||||
public T call(InternalContext context) throws ErrorsException {
|
} catch (InternalProvisionException e) {
|
||||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
throw e.addSource(dependency).toProvisionException();
|
||||||
try {
|
|
||||||
return binding.getInternalFactory().get(errors, context, dependency, false);
|
|
||||||
} finally {
|
} finally {
|
||||||
context.popStateAndSetDependency(previous);
|
currentContext.popStateAndSetDependency(previous);
|
||||||
}
|
currentContext.close();
|
||||||
}
|
|
||||||
});
|
|
||||||
errors.throwIfNewErrors(0);
|
|
||||||
return t;
|
|
||||||
} catch (ErrorsException e) {
|
|
||||||
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return binding.getInternalFactory().toString();
|
return internalFactory.toString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(final Key<T> key) {
|
public <T> Provider<T> getProvider(final Key<T> key) {
|
||||||
|
checkNotNull(key, "key");
|
||||||
Errors errors = new Errors(key);
|
Errors errors = new Errors(key);
|
||||||
try {
|
try {
|
||||||
Provider<T> result = getProviderOrThrow(Dependency.get(key), errors);
|
Provider<T> result = getProviderOrThrow(Dependency.get(key), errors);
|
||||||
|
@ -874,50 +914,16 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> T getInstance(Key<T> key) {
|
public <T> T getInstance(Key<T> key) {
|
||||||
return getProvider(key).get();
|
return getProvider(key).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> T getInstance(Class<T> type) {
|
public <T> T getInstance(Class<T> type) {
|
||||||
return getProvider(type).get();
|
return getProvider(type).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks up thread local context. Creates (and removes) a new context if necessary.
|
|
||||||
*/
|
|
||||||
<T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
|
|
||||||
Object[] reference = localContext.get();
|
|
||||||
if (reference == null) {
|
|
||||||
reference = new Object[1];
|
|
||||||
localContext.set(reference);
|
|
||||||
}
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
if (reference[0] == null) {
|
|
||||||
reference[0] = new InternalContext(options);
|
|
||||||
globalInternalContext.put(currentThread, (InternalContext) reference[0]);
|
|
||||||
try {
|
|
||||||
return callable.call((InternalContext) reference[0]);
|
|
||||||
} finally {
|
|
||||||
// Only clear contexts if this call created them.
|
|
||||||
reference[0] = null;
|
|
||||||
globalInternalContext.remove(currentThread);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Object previousGlobalInternalContext = globalInternalContext.get(currentThread);
|
|
||||||
globalInternalContext.put(currentThread, (InternalContext) reference[0]);
|
|
||||||
try {
|
|
||||||
// Someone else will clean up this local context.
|
|
||||||
return callable.call((InternalContext) reference[0]);
|
|
||||||
} finally {
|
|
||||||
if (previousGlobalInternalContext != null) {
|
|
||||||
globalInternalContext.put(currentThread, (InternalContext) previousGlobalInternalContext);
|
|
||||||
} else {
|
|
||||||
globalInternalContext.remove(currentThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(Injector.class)
|
return MoreObjects.toStringHelper(Injector.class)
|
||||||
|
@ -994,21 +1000,20 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
|
|
||||||
static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) {
|
static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) {
|
||||||
final Provider<T> provider = providedBinding.getProvider();
|
final Provider<T> provider = providedBinding.getProvider();
|
||||||
return new InternalFactory<Provider<T>>() {
|
return (context, dependency, linked) -> provider;
|
||||||
public Provider<T> get(Errors errors, InternalContext context, Dependency dependency, boolean linked) {
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Key<? extends T> getProvidedKey() {
|
public Key<? extends T> getProvidedKey() {
|
||||||
return providedBinding.getKey();
|
return providedBinding.getKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
throw new UnsupportedOperationException("This element represents a synthetic binding.");
|
throw new UnsupportedOperationException("This element represents a synthetic binding.");
|
||||||
}
|
}
|
||||||
|
@ -1021,6 +1026,7 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey()));
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey()));
|
||||||
}
|
}
|
||||||
|
@ -1050,8 +1056,10 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
final Binding<String> originalBinding;
|
final Binding<String> originalBinding;
|
||||||
final TypeConverterBinding typeConverterBinding;
|
final TypeConverterBinding typeConverterBinding;
|
||||||
|
|
||||||
ConvertedConstantBindingImpl(
|
ConvertedConstantBindingImpl(InjectorImpl injector,
|
||||||
InjectorImpl injector, Key<T> key, T value, Binding<String> originalBinding,
|
Key<T> key,
|
||||||
|
T value,
|
||||||
|
Binding<String> originalBinding,
|
||||||
TypeConverterBinding typeConverterBinding) {
|
TypeConverterBinding typeConverterBinding) {
|
||||||
super(injector, key, originalBinding.getSource(),
|
super(injector, key, originalBinding.getSource(),
|
||||||
new ConstantFactory<T>(Initializables.of(value)), Scoping.UNSCOPED);
|
new ConstantFactory<T>(Initializables.of(value)), Scoping.UNSCOPED);
|
||||||
|
@ -1066,26 +1074,32 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public T getValue() {
|
public T getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TypeConverterBinding getTypeConverterBinding() {
|
public TypeConverterBinding getTypeConverterBinding() {
|
||||||
return typeConverterBinding;
|
return typeConverterBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Key<String> getSourceKey() {
|
public Key<String> getSourceKey() {
|
||||||
return originalBinding.getKey();
|
return originalBinding.getKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey()));
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
throw new UnsupportedOperationException("This element represents a synthetic binding.");
|
throw new UnsupportedOperationException("This element represents a synthetic binding.");
|
||||||
}
|
}
|
||||||
|
@ -1116,27 +1130,4 @@ final class InjectorImpl implements Injector, Lookups {
|
||||||
return Objects.hashCode(getKey(), getScoping(), value);
|
return Objects.hashCode(getKey(), getScoping(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class BindingsMultimap {
|
|
||||||
final Map<TypeLiteral<?>, List<Binding<?>>> multimap = Maps.newHashMap();
|
|
||||||
|
|
||||||
<T> void put(TypeLiteral<T> type, Binding<T> binding) {
|
|
||||||
List<Binding<?>> bindingsForType = multimap.get(type);
|
|
||||||
if (bindingsForType == null) {
|
|
||||||
bindingsForType = Lists.newArrayList();
|
|
||||||
multimap.put(type, bindingsForType);
|
|
||||||
}
|
|
||||||
bindingsForType.add(binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// safe because we only put matching entries into the map
|
|
||||||
<T> List<Binding<T>> getAll(TypeLiteral<T> type) {
|
|
||||||
List<Binding<?>> bindings = multimap.get(type);
|
|
||||||
return bindings != null
|
|
||||||
? Collections.<Binding<T>>unmodifiableList((List) multimap.get(type))
|
|
||||||
: ImmutableList.<Binding<T>>of();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import com.google.inject.internal.util.Stopwatch;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.Element;
|
import com.google.inject.spi.Element;
|
||||||
import com.google.inject.spi.Elements;
|
import com.google.inject.spi.Elements;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
|
||||||
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||||
import com.google.inject.spi.PrivateElements;
|
import com.google.inject.spi.PrivateElements;
|
||||||
import com.google.inject.spi.ProvisionListenerBinding;
|
import com.google.inject.spi.ProvisionListenerBinding;
|
||||||
|
@ -35,11 +34,20 @@ final class InjectorShell {
|
||||||
private final List<Element> elements;
|
private final List<Element> elements;
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
|
private InjectorShell(List<Element> elements, InjectorImpl injector) {
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InjectorImpl getInjector() {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Element> getElements() {
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Injector is a special case because we allow both parent and child injectors to both have
|
* The Injector is a special case because we allow both parent and child injectors to both have
|
||||||
* a binding for that key.
|
* a binding for that key.
|
||||||
|
@ -48,44 +56,23 @@ final class InjectorShell {
|
||||||
Key<Injector> key = Key.get(Injector.class);
|
Key<Injector> key = Key.get(Injector.class);
|
||||||
InjectorFactory injectorFactory = new InjectorFactory(injector);
|
InjectorFactory injectorFactory = new InjectorFactory(injector);
|
||||||
injector.state.putBinding(key,
|
injector.state.putBinding(key,
|
||||||
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
|
new ProviderInstanceBindingImpl<>(injector, key, SourceProvider.UNKNOWN_SOURCE,
|
||||||
injectorFactory, Scoping.UNSCOPED, injectorFactory,
|
injectorFactory, Scoping.UNSCOPED, injectorFactory,
|
||||||
ImmutableSet.<InjectionPoint>of()));
|
ImmutableSet.of()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The Logger is a special case because it knows the injection point of the injected member. It's
|
|
||||||
* the only binding that does this.
|
|
||||||
*/
|
|
||||||
/*private static void bindLogger(InjectorImpl injector) {
|
|
||||||
Key<Logger> key = Key.get(Logger.class);
|
|
||||||
LoggerFactory loggerFactory = new LoggerFactory();
|
|
||||||
injector.state.putBinding(key,
|
|
||||||
new ProviderInstanceBindingImpl<Logger>(injector, key,
|
|
||||||
SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
|
|
||||||
loggerFactory, ImmutableSet.<InjectionPoint>of()));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private static void bindStage(InjectorImpl injector, Stage stage) {
|
private static void bindStage(InjectorImpl injector, Stage stage) {
|
||||||
Key<Stage> key = Key.get(Stage.class);
|
Key<Stage> key = Key.get(Stage.class);
|
||||||
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
|
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<>(
|
||||||
injector,
|
injector,
|
||||||
key,
|
key,
|
||||||
SourceProvider.UNKNOWN_SOURCE,
|
SourceProvider.UNKNOWN_SOURCE,
|
||||||
new ConstantFactory<Stage>(Initializables.of(stage)),
|
new ConstantFactory<>(Initializables.of(stage)),
|
||||||
ImmutableSet.<InjectionPoint>of(),
|
ImmutableSet.of(),
|
||||||
stage);
|
stage);
|
||||||
injector.state.putBinding(key, stageBinding);
|
injector.state.putBinding(key, stageBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
InjectorImpl getInjector() {
|
|
||||||
return injector;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> getElements() {
|
|
||||||
return elements;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Builder {
|
static class Builder {
|
||||||
private final List<Element> elements = Lists.newArrayList();
|
private final List<Element> elements = Lists.newArrayList();
|
||||||
private final List<Module> modules = Lists.newArrayList();
|
private final List<Module> modules = Lists.newArrayList();
|
||||||
|
@ -213,7 +200,7 @@ final class InjectorShell {
|
||||||
stopwatch.resetAndLog("Module annotated method scanners creation");
|
stopwatch.resetAndLog("Module annotated method scanners creation");
|
||||||
|
|
||||||
List<InjectorShell> injectorShells = Lists.newArrayList();
|
List<InjectorShell> injectorShells = Lists.newArrayList();
|
||||||
injectorShells.add(new InjectorShell(this, elements, injector));
|
injectorShells.add(new InjectorShell(elements, injector));
|
||||||
|
|
||||||
// recursively build child shells
|
// recursively build child shells
|
||||||
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
||||||
|
@ -241,11 +228,12 @@ final class InjectorShell {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector get() {
|
public Injector get() {
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
@ -255,24 +243,8 @@ final class InjectorShell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
|
|
||||||
public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
|
|
||||||
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
|
||||||
return injectionPoint == null
|
|
||||||
? Logger.getAnonymousLogger()
|
|
||||||
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logger get() {
|
|
||||||
return Logger.getAnonymousLogger();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return "Provider<Logger>";
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private static class RootModule implements Module {
|
private static class RootModule implements Module {
|
||||||
|
@Override
|
||||||
public void configure(Binder binder) {
|
public void configure(Binder binder) {
|
||||||
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
||||||
binder.bindScope(Singleton.class, SINGLETON);
|
binder.bindScope(Singleton.class, SINGLETON);
|
||||||
|
@ -287,6 +259,7 @@ final class InjectorShell {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void configure(Binder binder) {
|
public void configure(Binder binder) {
|
||||||
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
|
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
|
||||||
binding.applyTo(binder);
|
binding.applyTo(binder);
|
||||||
|
|
|
@ -5,20 +5,17 @@ import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.spi.BindingTargetVisitor;
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.HasDependencies;
|
import com.google.inject.spi.HasDependencies;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
import com.google.inject.spi.InstanceBinding;
|
import com.google.inject.spi.InstanceBinding;
|
||||||
import com.google.inject.util.Providers;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
||||||
|
|
||||||
final T instance;
|
final T instance;
|
||||||
final Provider<T> provider;
|
|
||||||
final ImmutableSet<InjectionPoint> injectionPoints;
|
final ImmutableSet<InjectionPoint> injectionPoints;
|
||||||
|
|
||||||
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
@ -27,7 +24,6 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
|
||||||
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
|
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
|
||||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.provider = Providers.of(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
||||||
|
@ -35,40 +31,41 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
|
||||||
super(source, key, scoping);
|
super(source, key, scoping);
|
||||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.provider = Providers.of(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Provider<T> getProvider() {
|
|
||||||
return this.provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public T getInstance() {
|
public T getInstance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<InjectionPoint> getInjectionPoints() {
|
public Set<InjectionPoint> getInjectionPoints() {
|
||||||
return injectionPoints;
|
return injectionPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return instance instanceof HasDependencies
|
return instance instanceof HasDependencies
|
||||||
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
||||||
: Dependency.forInjectionPoints(injectionPoints);
|
: Dependency.forInjectionPoints(injectionPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingImpl<T> withKey(Key<T> key) {
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
// instance bindings aren't scoped
|
// instance bindings aren't scoped
|
||||||
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
||||||
|
|
|
@ -1,37 +1,76 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.DependencyAndSource;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal context. Used to coordinate injections and support circular
|
* Internal context. Used to coordinate injections and support circular
|
||||||
* dependencies.
|
* dependencies.
|
||||||
*/
|
*/
|
||||||
final class InternalContext {
|
final class InternalContext implements AutoCloseable {
|
||||||
|
|
||||||
private final InjectorOptions options;
|
private final InjectorOptions options;
|
||||||
/**
|
|
||||||
* Keeps track of the hierarchy of types needed during injection.
|
private final Map<Object, ConstructionContext<?>> constructionContexts =
|
||||||
*/
|
new IdentityHashMap<>();
|
||||||
private final DependencyStack state = new DependencyStack();
|
|
||||||
private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of the type that is currently being requested for injection.
|
* Keeps track of the type that is currently being requested for injection.
|
||||||
*/
|
*/
|
||||||
private Dependency<?> dependency;
|
private Dependency<?> dependency;
|
||||||
|
|
||||||
InternalContext(InjectorOptions options) {
|
/**
|
||||||
|
* Keeps track of the hierarchy of types needed during injection.
|
||||||
|
*
|
||||||
|
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
|
||||||
|
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
|
||||||
|
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
|
||||||
|
*/
|
||||||
|
private Object[] dependencyStack = new Object[16];
|
||||||
|
|
||||||
|
private int dependencyStackSize = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of times {@link #enter()} has been called + 1 for initial construction.
|
||||||
|
*/
|
||||||
|
private int enterCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single element array to clear when the {@link #enterCount} hits {@code 0}.
|
||||||
|
*
|
||||||
|
* <p>This is the value stored in the {@code InjectorImpl.localContext} thread local.
|
||||||
|
*/
|
||||||
|
private final Object[] toClear;
|
||||||
|
|
||||||
|
InternalContext(InjectorOptions options, Object[] toClear) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
this.toClear = toClear;
|
||||||
|
this.enterCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Should only be called by InjectorImpl.enterContext(). */
|
||||||
|
void enter() {
|
||||||
|
enterCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Should be called any any method that received an instance via InjectorImpl.enterContext(). */
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
int newCount = --enterCount;
|
||||||
|
if (newCount < 0) {
|
||||||
|
throw new IllegalStateException("Called close() too many times");
|
||||||
|
}
|
||||||
|
if (newCount == 0) {
|
||||||
|
toClear[0] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public InjectorOptions getInjectorOptions() {
|
public InjectorOptions getInjectorOptions() {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +96,7 @@ final class InternalContext {
|
||||||
public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
|
public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
|
||||||
Dependency<?> previous = this.dependency;
|
Dependency<?> previous = this.dependency;
|
||||||
this.dependency = dependency;
|
this.dependency = dependency;
|
||||||
state.add(dependency, source);
|
doPushState(dependency, source);
|
||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +104,7 @@ final class InternalContext {
|
||||||
* Pops the current state & sets the new dependency.
|
* Pops the current state & sets the new dependency.
|
||||||
*/
|
*/
|
||||||
public void popStateAndSetDependency(Dependency<?> newDependency) {
|
public void popStateAndSetDependency(Dependency<?> newDependency) {
|
||||||
state.pop();
|
popState();
|
||||||
this.dependency = newDependency;
|
this.dependency = newDependency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,64 +112,29 @@ final class InternalContext {
|
||||||
* Adds to the state without setting the dependency.
|
* Adds to the state without setting the dependency.
|
||||||
*/
|
*/
|
||||||
public void pushState(Key<?> key, Object source) {
|
public void pushState(Key<?> key, Object source) {
|
||||||
state.add(key, source);
|
doPushState(key, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doPushState(Object dependencyOrKey, Object source) {
|
||||||
|
int localSize = dependencyStackSize;
|
||||||
|
Object[] localStack = dependencyStack;
|
||||||
|
if (localStack.length < localSize + 2) {
|
||||||
|
localStack = dependencyStack =
|
||||||
|
java.util.Arrays.copyOf(localStack, (localStack.length * 3) / 2 + 2);
|
||||||
|
}
|
||||||
|
localStack[localSize++] = dependencyOrKey;
|
||||||
|
localStack[localSize++] = source;
|
||||||
|
dependencyStackSize = localSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pops from the state without setting a dependency.
|
* Pops from the state without setting a dependency.
|
||||||
*/
|
*/
|
||||||
public void popState() {
|
public void popState() {
|
||||||
state.pop();
|
// N.B. we don't null out the array entries. It isn't necessary since all the objects in the
|
||||||
}
|
// array (Key, Dependency, or Binding source objects) are all tied to the lifetime of the
|
||||||
|
// injector, which is greater than the lifetime of this object. So removing them from the array
|
||||||
/**
|
// doesn't matter.
|
||||||
* Returns the current dependency chain (all the state).
|
dependencyStackSize -= 2;
|
||||||
*/
|
|
||||||
public List<DependencyAndSource> getDependencyChain() {
|
|
||||||
ImmutableList.Builder<DependencyAndSource> builder = ImmutableList.builder();
|
|
||||||
for (int i = 0; i < state.size(); i += 2) {
|
|
||||||
Object evenEntry = state.get(i);
|
|
||||||
Dependency<?> dependency;
|
|
||||||
if (evenEntry instanceof Key) {
|
|
||||||
dependency = Dependency.get((Key<?>) evenEntry);
|
|
||||||
} else {
|
|
||||||
dependency = (Dependency<?>) evenEntry;
|
|
||||||
}
|
|
||||||
builder.add(new DependencyAndSource(dependency, state.get(i + 1)));
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of the hierarchy of types needed during injection.
|
|
||||||
*
|
|
||||||
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
|
|
||||||
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
|
|
||||||
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
|
|
||||||
*/
|
|
||||||
private static final class DependencyStack {
|
|
||||||
private Object[] elements = new Object[16];
|
|
||||||
private int size = 0;
|
|
||||||
|
|
||||||
public void add(Object dependencyOrKey, Object source) {
|
|
||||||
if (elements.length < size + 2) {
|
|
||||||
elements = Arrays.copyOf(elements, (elements.length * 3) / 2 + 2);
|
|
||||||
}
|
|
||||||
elements[size++] = dependencyOrKey;
|
|
||||||
elements[size++] = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pop() {
|
|
||||||
elements[--size] = null;
|
|
||||||
elements[--size] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object get(int i) {
|
|
||||||
return elements[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,9 @@ interface InternalFactory<T> {
|
||||||
*
|
*
|
||||||
* @param context of this injection
|
* @param context of this injection
|
||||||
* @param linked true if getting as a result of a linked binding
|
* @param linked true if getting as a result of a linked binding
|
||||||
* @return instance to be injected
|
* @return instance that was created
|
||||||
* @throws com.google.inject.internal.ErrorsException if a value cannot be provided
|
* @throws InternalProvisionException if a value cannot be provided
|
||||||
*/
|
*/
|
||||||
T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
throws ErrorsException;
|
throws InternalProvisionException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
|
||||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
private final Initializable<? extends javax.inject.Provider<? extends T>> initializable;
|
private final Initializable<? extends javax.inject.Provider<? extends T>> initializable;
|
||||||
|
|
||||||
public InternalFactoryToInitializableAdapter(
|
InternalFactoryToInitializableAdapter(
|
||||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable,
|
Initializable<? extends javax.inject.Provider<? extends T>> initializable,
|
||||||
Object source, ProvisionListenerStackCallback<T> provisionCallback) {
|
Object source, ProvisionListenerStackCallback<T> provisionCallback) {
|
||||||
super(source);
|
super(source);
|
||||||
|
@ -22,19 +22,20 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
|
||||||
this.initializable = checkNotNull(initializable, "provider");
|
this.initializable = checkNotNull(initializable, "provider");
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
return circularGet(initializable.get(errors), errors, context, dependency,
|
throws InternalProvisionException {
|
||||||
provisionCallback);
|
return circularGet(initializable.get(), context, dependency, provisionCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
|
protected T provision(javax.inject.Provider<? extends T> provider,
|
||||||
Dependency<?> dependency, ConstructionContext<T> constructionContext) throws ErrorsException {
|
Dependency<?> dependency,
|
||||||
|
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
return super.provision(provider, errors, dependency, constructionContext);
|
return super.provision(provider, dependency, constructionContext);
|
||||||
} catch (RuntimeException userException) {
|
} catch (RuntimeException userException) {
|
||||||
throw errors.withSource(source).errorInProvider(userException).toException();
|
throw InternalProvisionException.errorInProvider(userException).addSource(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,18 +10,22 @@ final class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> {
|
||||||
private final Provider<? extends T> provider;
|
private final Provider<? extends T> provider;
|
||||||
private final Object source;
|
private final Object source;
|
||||||
|
|
||||||
public InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
|
InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
|
||||||
this.provider = checkNotNull(provider, "provider");
|
this.provider = checkNotNull(provider, "provider");
|
||||||
this.source = checkNotNull(source, "source");
|
this.source = checkNotNull(source, "source");
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
// TODO(sameb): Does this need to push state into the context?
|
throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
return errors.checkForNull(provider.get(), source, dependency);
|
T t = provider.get();
|
||||||
|
if (t == null && !dependency.isNullable()) {
|
||||||
|
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
} catch (RuntimeException userException) {
|
} catch (RuntimeException userException) {
|
||||||
throw errors.withSource(source).errorInProvider(userException).toException();
|
throw InternalProvisionException.errorInProvider(userException).addSource(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,19 +66,13 @@ public class InternalFlags {
|
||||||
private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue,
|
private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue,
|
||||||
T secureValue) {
|
T secureValue) {
|
||||||
Class<T> enumType = defaultValue.getDeclaringClass();
|
Class<T> enumType = defaultValue.getDeclaringClass();
|
||||||
String value = null;
|
|
||||||
try {
|
try {
|
||||||
value = AccessController.doPrivileged(new PrivilegedAction<String>() {
|
String value = AccessController.doPrivileged((PrivilegedAction<String>) ()
|
||||||
public String run() {
|
-> System.getProperty(name));
|
||||||
return System.getProperty(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue;
|
return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue;
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
return secureValue;
|
return secureValue;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
//logger.warning(value + " is not a valid flag value for " + name + ". "
|
|
||||||
// + " Values must be one of " + Arrays.asList(enumType.getEnumConstants()));
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
|
@ -13,9 +11,12 @@ import com.google.inject.Stage;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.internal.util.Stopwatch;
|
import com.google.inject.internal.util.Stopwatch;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -64,7 +65,7 @@ public final class InternalInjectorCreator {
|
||||||
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
|
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
|
||||||
* stage to the stage of {@code parent}.
|
* stage to the stage of {@code parent}.
|
||||||
*/
|
*/
|
||||||
public InternalInjectorCreator parentInjector(InjectorImpl parent) {
|
InternalInjectorCreator parentInjector(InjectorImpl parent) {
|
||||||
shellBuilder.parent(parent);
|
shellBuilder.parent(parent);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -102,36 +103,32 @@ public final class InternalInjectorCreator {
|
||||||
private void initializeStatically() {
|
private void initializeStatically() {
|
||||||
bindingData.initializeBindings();
|
bindingData.initializeBindings();
|
||||||
stopwatch.resetAndLog("Binding initialization");
|
stopwatch.resetAndLog("Binding initialization");
|
||||||
|
|
||||||
for (InjectorShell shell : shells) {
|
for (InjectorShell shell : shells) {
|
||||||
shell.getInjector().index();
|
shell.getInjector().index();
|
||||||
}
|
}
|
||||||
stopwatch.resetAndLog("Binding indexing");
|
stopwatch.resetAndLog("Binding indexing");
|
||||||
|
|
||||||
injectionRequestProcessor.process(shells);
|
injectionRequestProcessor.process(shells);
|
||||||
stopwatch.resetAndLog("Collecting injection requests");
|
stopwatch.resetAndLog("Collecting injection requests");
|
||||||
|
|
||||||
bindingData.runCreationListeners(errors);
|
bindingData.runCreationListeners(errors);
|
||||||
stopwatch.resetAndLog("Binding validation");
|
stopwatch.resetAndLog("Binding validation");
|
||||||
|
|
||||||
injectionRequestProcessor.validate();
|
injectionRequestProcessor.validate();
|
||||||
stopwatch.resetAndLog("Static validation");
|
stopwatch.resetAndLog("Static validation");
|
||||||
|
|
||||||
initializer.validateOustandingInjections(errors);
|
initializer.validateOustandingInjections(errors);
|
||||||
stopwatch.resetAndLog("Instance member validation");
|
stopwatch.resetAndLog("Instance member validation");
|
||||||
|
|
||||||
new LookupProcessor(errors).process(shells);
|
new LookupProcessor(errors).process(shells);
|
||||||
for (InjectorShell shell : shells) {
|
for (InjectorShell shell : shells) {
|
||||||
((DeferredLookups) shell.getInjector().lookups).initialize(errors);
|
((DeferredLookups) shell.getInjector().lookups).initialize(errors);
|
||||||
}
|
}
|
||||||
stopwatch.resetAndLog("Provider verification");
|
stopwatch.resetAndLog("Provider verification");
|
||||||
|
// This needs to come late since some user bindings rely on requireBinding calls to create
|
||||||
|
// jit bindings during the LookupProcessor.
|
||||||
|
bindingData.initializeDelayedBindings();
|
||||||
|
stopwatch.resetAndLog("Delayed Binding initialization");
|
||||||
for (InjectorShell shell : shells) {
|
for (InjectorShell shell : shells) {
|
||||||
if (!shell.getElements().isEmpty()) {
|
if (!shell.getElements().isEmpty()) {
|
||||||
throw new AssertionError("Failed to execute " + shell.getElements());
|
throw new AssertionError("Failed to execute " + shell.getElements());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.throwCreationExceptionIfErrorsExist();
|
errors.throwCreationExceptionIfErrorsExist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,31 +167,25 @@ public final class InternalInjectorCreator {
|
||||||
*/
|
*/
|
||||||
void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
|
void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
|
||||||
@SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
|
@SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
|
||||||
Iterable<BindingImpl<?>> candidateBindings = ImmutableList.copyOf(Iterables.concat(
|
Collection<BindingImpl<?>> bindingsAtThisLevel =
|
||||||
(Collection) injector.state.getExplicitBindingsThisLevel().values(),
|
(Collection) injector.state.getExplicitBindingsThisLevel().values();
|
||||||
injector.jitBindings.values()));
|
List<BindingImpl<?>> candidateBindings = new ArrayList<>(bindingsAtThisLevel);
|
||||||
for (final BindingImpl<?> binding : candidateBindings) {
|
synchronized (injector.state.lock()) {
|
||||||
|
// jit bindings must be accessed while holding the lock.
|
||||||
|
candidateBindings.addAll(injector.jitBindings.values());
|
||||||
|
}
|
||||||
|
try (InternalContext context = injector.enterContext()) {
|
||||||
|
for (BindingImpl<?> binding : candidateBindings) {
|
||||||
if (isEagerSingleton(injector, binding, stage)) {
|
if (isEagerSingleton(injector, binding, stage)) {
|
||||||
try {
|
|
||||||
injector.callInContext(new ContextualCallable<Void>() {
|
|
||||||
Dependency<?> dependency = Dependency.get(binding.getKey());
|
Dependency<?> dependency = Dependency.get(binding.getKey());
|
||||||
|
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
|
||||||
public Void call(InternalContext context) {
|
|
||||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
|
||||||
Errors errorsForBinding = errors.withSource(dependency);
|
|
||||||
try {
|
try {
|
||||||
binding.getInternalFactory().get(errorsForBinding, context, dependency, false);
|
binding.getInternalFactory().get(context, dependency, false);
|
||||||
} catch (ErrorsException e) {
|
} catch (InternalProvisionException e) {
|
||||||
errorsForBinding.merge(e.getErrors());
|
errors.withSource(dependency).merge(e);
|
||||||
} finally {
|
} finally {
|
||||||
context.popStateAndSetDependency(previous);
|
context.popStateAndSetDependency(previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (ErrorsException e) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,10 +198,11 @@ public final class InternalInjectorCreator {
|
||||||
|
|
||||||
// handle a corner case where a child injector links to a binding in a parent injector, and
|
// handle a corner case where a child injector links to a binding in a parent injector, and
|
||||||
// that binding is singleton. We won't catch this otherwise because we only iterate the child's
|
// that binding is singleton. We won't catch this otherwise because we only iterate the child's
|
||||||
// bindings.
|
// bindings. This only applies if the linked binding is not itself scoped.
|
||||||
if (binding instanceof LinkedBindingImpl) {
|
if (binding instanceof LinkedBindingImpl) {
|
||||||
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
|
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
|
||||||
return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
|
return binding.getScoping().isNoScope() &&
|
||||||
|
isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -226,80 +218,108 @@ public final class InternalInjectorCreator {
|
||||||
this.delegateInjector = delegateInjector;
|
this.delegateInjector = delegateInjector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void injectMembers(Object o) {
|
public void injectMembers(Object o) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.injectMembers(Object) is not supported in Stage.TOOL");
|
"Injector.injectMembers(Object) is not supported in Stage.TOOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Key<?>, Binding<?>> getBindings() {
|
public Map<Key<?>, Binding<?>> getBindings() {
|
||||||
return this.delegateInjector.getBindings();
|
return this.delegateInjector.getBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Key<?>, Binding<?>> getAllBindings() {
|
public Map<Key<?>, Binding<?>> getAllBindings() {
|
||||||
return this.delegateInjector.getAllBindings();
|
return this.delegateInjector.getAllBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Binding<T> getBinding(Key<T> key) {
|
public <T> Binding<T> getBinding(Key<T> key) {
|
||||||
return this.delegateInjector.getBinding(key);
|
return this.delegateInjector.getBinding(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Binding<T> getBinding(Class<T> type) {
|
public <T> Binding<T> getBinding(Class<T> type) {
|
||||||
return this.delegateInjector.getBinding(type);
|
return this.delegateInjector.getBinding(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Binding<T> getExistingBinding(Key<T> key) {
|
public <T> Binding<T> getExistingBinding(Key<T> key) {
|
||||||
return this.delegateInjector.getExistingBinding(key);
|
return this.delegateInjector.getExistingBinding(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
|
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
|
||||||
return this.delegateInjector.findBindingsByType(type);
|
return this.delegateInjector.findBindingsByType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector getParent() {
|
public Injector getParent() {
|
||||||
return delegateInjector.getParent();
|
return delegateInjector.getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
||||||
return delegateInjector.createChildInjector(modules);
|
return delegateInjector.createChildInjector(modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector createChildInjector(Module... modules) {
|
public Injector createChildInjector(Module... modules) {
|
||||||
return delegateInjector.createChildInjector(modules);
|
return delegateInjector.createChildInjector(modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
||||||
return delegateInjector.getScopeBindings();
|
return delegateInjector.getScopeBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<TypeConverterBinding> getTypeConverterBindings() {
|
public Set<TypeConverterBinding> getTypeConverterBindings() {
|
||||||
return delegateInjector.getTypeConverterBindings();
|
return delegateInjector.getTypeConverterBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Element> getElements() {
|
||||||
|
return delegateInjector.getElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints() {
|
||||||
|
return delegateInjector.getAllMembersInjectorInjectionPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Key<T> key) {
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
|
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Class<T> type) {
|
public <T> Provider<T> getProvider(Class<T> type) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
|
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
|
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
|
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> T getInstance(Key<T> key) {
|
public <T> T getInstance(Key<T> key) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
|
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> T getInstance(Class<T> type) {
|
public <T> T getInstance(Class<T> type) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
|
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.HasDependencies;
|
||||||
|
import com.google.inject.spi.ProviderWithExtensionVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ProviderInstanceBindingImpl} for implementing 'native' guice extensions.
|
||||||
|
*
|
||||||
|
* <p>Beyond the normal binding contract that is mostly handled by our baseclass, this also
|
||||||
|
* implements {@link DelayedInitialize} in order to initialize factory state.
|
||||||
|
*/
|
||||||
|
final class InternalProviderInstanceBindingImpl<T> extends ProviderInstanceBindingImpl<T>
|
||||||
|
implements DelayedInitialize {
|
||||||
|
enum InitializationTiming {
|
||||||
|
/** This factory can be initialized eagerly. This should be the case for most things. */
|
||||||
|
EAGER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization of this factory should be delayed until after all other static initialization
|
||||||
|
* completes. This will be useful for factories that need to call {@link
|
||||||
|
* InjectorImpl#getExistingBinding(Key)} to not create jit bindings, but also want to be able to
|
||||||
|
* conditionally consume jit bindings created by other other bindings.
|
||||||
|
*/
|
||||||
|
DELAYED
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Factory<T> originalFactory;
|
||||||
|
|
||||||
|
InternalProviderInstanceBindingImpl(
|
||||||
|
InjectorImpl injector,
|
||||||
|
Key<T> key,
|
||||||
|
Object source,
|
||||||
|
Factory<T> originalFactory,
|
||||||
|
InternalFactory<? extends T> scopedFactory,
|
||||||
|
Scoping scoping) {
|
||||||
|
super(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
source,
|
||||||
|
scopedFactory,
|
||||||
|
scoping,
|
||||||
|
originalFactory,
|
||||||
|
ImmutableSet.of());
|
||||||
|
this.originalFactory = originalFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializationTiming getInitializationTiming() {
|
||||||
|
return originalFactory.initializationTiming;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(final InjectorImpl injector, final Errors errors) throws ErrorsException {
|
||||||
|
originalFactory.source = getSource();
|
||||||
|
originalFactory.provisionCallback = injector.provisionListenerStore.get(this);
|
||||||
|
// For these kinds of providers, the 'user supplied provider' is really 'guice supplied'
|
||||||
|
// So make our user supplied provider just delegate to the guice supplied one.
|
||||||
|
originalFactory.delegateProvider = getProvider();
|
||||||
|
originalFactory.initialize(injector, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base factory implementation. Any Factories that delegate to other bindings should use the
|
||||||
|
* {@code CyclicFactory} subclass, but trivial factories can use this one.
|
||||||
|
*/
|
||||||
|
abstract static class Factory<T> implements InternalFactory<T>, Provider<T>, HasDependencies {
|
||||||
|
private final InitializationTiming initializationTiming;
|
||||||
|
private Object source;
|
||||||
|
private Provider<T> delegateProvider;
|
||||||
|
ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
|
||||||
|
Factory(InitializationTiming initializationTiming) {
|
||||||
|
this.initializationTiming = initializationTiming;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The binding source.
|
||||||
|
*
|
||||||
|
* <p>May be useful for augmenting runtime error messages.
|
||||||
|
*
|
||||||
|
* <p>Note: this will return {#code null} until {@link #initialize(InjectorImpl, Errors)} has
|
||||||
|
* already been called.
|
||||||
|
*/
|
||||||
|
final Object getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback that allows for implementations to fetch dependencies on other bindings.
|
||||||
|
*
|
||||||
|
* <p>Will be called exactly once, prior to any call to {@link #doProvision}.
|
||||||
|
*/
|
||||||
|
abstract void initialize(InjectorImpl injector, Errors errors) throws ErrorsException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final T get() {
|
||||||
|
Provider<T> local = delegateProvider;
|
||||||
|
if (local == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"This Provider cannot be used until the Injector has been created.");
|
||||||
|
}
|
||||||
|
return local.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(final InternalContext context, final Dependency<?> dependency, boolean linked)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
if (provisionCallback == null) {
|
||||||
|
return doProvision(context, dependency);
|
||||||
|
} else {
|
||||||
|
return provisionCallback.provision(context, () -> doProvision(context, dependency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates an object to be injected.
|
||||||
|
*
|
||||||
|
* @throws com.google.inject.internal.InternalProvisionException if a value cannot be provided
|
||||||
|
* @return instance to be injected
|
||||||
|
*/
|
||||||
|
protected abstract T doProvision(InternalContext context, Dependency<?> dependency)
|
||||||
|
throws InternalProvisionException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An base factory implementation that can be extended to provide a specialized implementation of
|
||||||
|
* a {@link ProviderWithExtensionVisitor} and also implements {@link InternalFactory}
|
||||||
|
*/
|
||||||
|
abstract static class CyclicFactory<T> extends Factory<T> {
|
||||||
|
|
||||||
|
CyclicFactory(InitializationTiming initializationTiming) {
|
||||||
|
super(initializationTiming);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final T get(
|
||||||
|
final InternalContext context, final Dependency<?> dependency, boolean linked)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||||
|
// We have a circular reference between bindings. Return a proxy.
|
||||||
|
if (constructionContext.isConstructing()) {
|
||||||
|
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T proxyType =
|
||||||
|
(T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
|
||||||
|
return proxyType;
|
||||||
|
}
|
||||||
|
// Optimization: Don't go through the callback stack if no one's listening.
|
||||||
|
constructionContext.startConstruction();
|
||||||
|
try {
|
||||||
|
if (provisionCallback == null) {
|
||||||
|
return provision(dependency, context, constructionContext);
|
||||||
|
} else {
|
||||||
|
return provisionCallback.provision(context, () -> provision(dependency, context, constructionContext));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
constructionContext.removeCurrentReference();
|
||||||
|
constructionContext.finishConstruction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T provision(
|
||||||
|
Dependency<?> dependency,
|
||||||
|
InternalContext context,
|
||||||
|
ConstructionContext<T> constructionContext)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
try {
|
||||||
|
T t = doProvision(context, dependency);
|
||||||
|
constructionContext.setProxyDelegates(t);
|
||||||
|
return t;
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(getSource());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw InternalProvisionException.errorInProvider(t).addSource(getSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.ProvisionException;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.util.SourceProvider;
|
||||||
|
import com.google.inject.internal.util.StackTraceElements;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.InjectionListener;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checked exception for provisioning errors.
|
||||||
|
*
|
||||||
|
* <p>This is the internal dual of {@link ProvisionException}, similar to the relationship between
|
||||||
|
* {@link com.google.inject.ConfigurationException} and {@link ErrorsException}. This is useful for
|
||||||
|
* several reasons:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Since it is a checked exception, we get some assistance from the java compiler in ensuring
|
||||||
|
* that we correctly handle it everywhere. ProvisionException is unchecked.
|
||||||
|
* <li>Since this is an internal package, we can add useful construction and mutation APIs that
|
||||||
|
* would be undesirable in a public supported API.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>This exception will be thrown when errors are encountered during provisioning, ErrorsException
|
||||||
|
* will continue to be used for errors that are encountered during provisioning and both make use of
|
||||||
|
* the {@link Message} as the core model.
|
||||||
|
*
|
||||||
|
* <p>NOTE: this object stores a list of messages but in the most common case the cardinality will
|
||||||
|
* be 1. The only time that multiple errors might be reported via this mechanism is when {@link
|
||||||
|
* #errorInUserCode} is called with an exception that holds multiple errors (like
|
||||||
|
* ProvisionException).
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public final class InternalProvisionException extends Exception {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(Guice.class.getName());
|
||||||
|
|
||||||
|
private static final Set<Dependency<?>> warnedDependencies =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||||
|
|
||||||
|
|
||||||
|
public static InternalProvisionException circularDependenciesDisabled(Class<?> expectedType) {
|
||||||
|
return create(
|
||||||
|
"Found a circular dependency involving %s, and circular dependencies are disabled.",
|
||||||
|
expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException cannotProxyClass(Class<?> expectedType) {
|
||||||
|
return create(
|
||||||
|
"Tried proxying %s to support a circular dependency, but it is not an interface.",
|
||||||
|
expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException create(String format, Object... arguments) {
|
||||||
|
return new InternalProvisionException(Messages.create(format, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException errorInUserCode(
|
||||||
|
Throwable cause, String messageFormat, Object... arguments) {
|
||||||
|
Collection<Message> messages = Errors.getMessagesFromThrowable(cause);
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
// TODO(lukes): it seems like we are dropping some valuable context here..
|
||||||
|
// consider eliminating this special case
|
||||||
|
return new InternalProvisionException(messages);
|
||||||
|
} else {
|
||||||
|
return new InternalProvisionException(Messages.create(cause, messageFormat, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException subtypeNotProvided(
|
||||||
|
Class<? extends javax.inject.Provider<?>> providerType, Class<?> type) {
|
||||||
|
return create("%s doesn't provide instances of %s.", providerType, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException errorInProvider(Throwable cause) {
|
||||||
|
return errorInUserCode(cause, "Error in custom provider, %s", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException errorInjectingMethod(Throwable cause) {
|
||||||
|
return errorInUserCode(cause, "Error injecting method, %s", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException errorInjectingConstructor(Throwable cause) {
|
||||||
|
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException errorInUserInjector(
|
||||||
|
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
|
||||||
|
return errorInUserCode(
|
||||||
|
cause, "Error injecting %s using %s.%n Reason: %s", type, listener, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException jitDisabled(Key<?> key) {
|
||||||
|
return create("Explicit bindings are required and %s is not explicitly bound.", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalProvisionException errorNotifyingInjectionListener(
|
||||||
|
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
|
||||||
|
return errorInUserCode(
|
||||||
|
cause, "Error notifying InjectionListener %s of %s.%n Reason: %s", listener, type, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code value} if it is non-null or allowed to be null. Otherwise a message is added and
|
||||||
|
* an {@code InternalProvisionException} is thrown.
|
||||||
|
*/
|
||||||
|
static void onNullInjectedIntoNonNullableDependency(Object source, Dependency<?> dependency)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
|
||||||
|
if (dependency.getInjectionPoint().getMember() instanceof Method) {
|
||||||
|
Method annotated = (Method) dependency.getInjectionPoint().getMember();
|
||||||
|
if (annotated.isAnnotationPresent(Provides.class)) {
|
||||||
|
switch (InternalFlags.getNullableProvidesOption()) {
|
||||||
|
case ERROR:
|
||||||
|
break; // break out & let the below exception happen
|
||||||
|
case IGNORE:
|
||||||
|
return; // user doesn't care about injecting nulls to non-@Nullables.
|
||||||
|
case WARN:
|
||||||
|
// Warn only once, otherwise we spam logs too much.
|
||||||
|
if (warnedDependencies.add(dependency)) {
|
||||||
|
logger.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"Guice injected null into {0} (a {1}), please mark it @Nullable."
|
||||||
|
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
|
||||||
|
+ " error.",
|
||||||
|
new Object[] {
|
||||||
|
Messages.formatParameter(dependency), Messages.convert(dependency.getKey())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object formattedDependency =
|
||||||
|
(dependency.getParameterIndex() != -1)
|
||||||
|
? Messages.formatParameter(dependency)
|
||||||
|
: StackTraceElements.forMember(dependency.getInjectionPoint().getMember());
|
||||||
|
|
||||||
|
throw InternalProvisionException.create(
|
||||||
|
"null returned by binding at %s%n but %s is not @Nullable", source, formattedDependency)
|
||||||
|
.addSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Object> sourcesToPrepend = new ArrayList<>();
|
||||||
|
private final ImmutableList<Message> errors;
|
||||||
|
|
||||||
|
private InternalProvisionException(Message error) {
|
||||||
|
this(ImmutableList.of(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InternalProvisionException(Iterable<Message> errors) {
|
||||||
|
this.errors = ImmutableList.copyOf(errors);
|
||||||
|
checkArgument(!this.errors.isEmpty(), "Can't create a provision exception with no errors");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends the given {@code source} to the stack of binding sources for the errors reported in
|
||||||
|
* this exception.
|
||||||
|
*
|
||||||
|
* <p>See {@link Errors#withSource(Object)}
|
||||||
|
*
|
||||||
|
* <p>It is expected that this method is called as the exception propagates up the stack.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
InternalProvisionException addSource(Object source) {
|
||||||
|
if (source == SourceProvider.UNKNOWN_SOURCE) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
int sz = sourcesToPrepend.size();
|
||||||
|
if (sz > 0 && sourcesToPrepend.get(sz - 1) == source) {
|
||||||
|
// This is for when there are two identical sources added in a row. This behavior is copied
|
||||||
|
// from Errors.withSource where it can happen when an constructor/provider method throws an
|
||||||
|
// exception
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
sourcesToPrepend.add(source);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableList<Message> getErrors() {
|
||||||
|
ImmutableList.Builder<Message> builder = ImmutableList.builder();
|
||||||
|
// reverse them since sources are added as the exception propagates (so the first source is the
|
||||||
|
// last one added)
|
||||||
|
List<Object> newSources = Lists.reverse(sourcesToPrepend);
|
||||||
|
for (Message error : errors) {
|
||||||
|
builder.add(Messages.mergeSources(newSources, error));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns this exception convered to a ProvisionException. */
|
||||||
|
public ProvisionException toProvisionException() {
|
||||||
|
return new ProvisionException(getErrors());
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,9 @@ import java.util.Set;
|
||||||
final class LinkedProviderBindingImpl<T>
|
final class LinkedProviderBindingImpl<T>
|
||||||
extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize {
|
extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize {
|
||||||
|
|
||||||
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||||
final DelayedInitialize delayedInitializer;
|
|
||||||
|
private final DelayedInitialize delayedInitializer;
|
||||||
|
|
||||||
private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
|
@ -27,7 +28,7 @@ final class LinkedProviderBindingImpl<T>
|
||||||
this.delayedInitializer = delayedInitializer;
|
this.delayedInitializer = delayedInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||||
this(injector, key, source, internalFactory, scoping, providerKey, null);
|
this(injector, key, source, internalFactory, scoping, providerKey, null);
|
||||||
|
@ -44,36 +45,43 @@ final class LinkedProviderBindingImpl<T>
|
||||||
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
|
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey,
|
Key<? extends javax.inject.Provider<? extends T>> providerKey,
|
||||||
DelayedInitialize delayedInitializer) {
|
DelayedInitialize delayedInitializer) {
|
||||||
return new LinkedProviderBindingImpl<T>(injector, key, source, internalFactory, scoping,
|
return new LinkedProviderBindingImpl<>(injector, key, source, internalFactory, scoping,
|
||||||
providerKey, delayedInitializer);
|
providerKey, delayedInitializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
|
public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
|
||||||
return providerKey;
|
return providerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
if (delayedInitializer != null) {
|
if (delayedInitializer != null) {
|
||||||
delayedInitializer.initialize(injector, errors);
|
delayedInitializer.initialize(injector, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
|
return ImmutableSet.of(Dependency.get(providerKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
return new LinkedProviderBindingImpl<T>(getSource(), getKey(), scoping, providerKey);
|
return new LinkedProviderBindingImpl<>(getSource(), getKey(), scoping, providerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingImpl<T> withKey(Key<T> key) {
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey);
|
return new LinkedProviderBindingImpl<>(getSource(), key, getScoping(), providerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
getScoping().applyTo(binder.withSource(getSource())
|
getScoping().applyTo(binder.withSource(getSource())
|
||||||
.bind(getKey()).toProvider(getProviderKey()));
|
.bind(getKey()).toProvider(getProviderKey()));
|
||||||
|
|
|
@ -13,68 +13,68 @@ import com.google.inject.spi.InjectionPoint;
|
||||||
* Injects members of instances of a given type.
|
* Injects members of instances of a given type.
|
||||||
*/
|
*/
|
||||||
final class MembersInjectorImpl<T> implements MembersInjector<T> {
|
final class MembersInjectorImpl<T> implements MembersInjector<T> {
|
||||||
private final TypeLiteral<T> typeLiteral;
|
|
||||||
private final InjectorImpl injector;
|
|
||||||
private final ImmutableList<SingleMemberInjector> memberInjectors;
|
|
||||||
private final ImmutableSet<MembersInjector<? super T>> userMembersInjectors;
|
|
||||||
private final ImmutableSet<InjectionListener<? super T>> injectionListeners;
|
|
||||||
|
|
||||||
MembersInjectorImpl(InjectorImpl injector, TypeLiteral<T> typeLiteral,
|
private final TypeLiteral<T> typeLiteral;
|
||||||
EncounterImpl<T> encounter, ImmutableList<SingleMemberInjector> memberInjectors) {
|
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
|
private final ImmutableList<SingleMemberInjector> memberInjectors;
|
||||||
|
|
||||||
|
private final ImmutableList<MembersInjector<? super T>> userMembersInjectors;
|
||||||
|
|
||||||
|
private final ImmutableList<InjectionListener<? super T>> injectionListeners;
|
||||||
|
|
||||||
|
MembersInjectorImpl(InjectorImpl injector,
|
||||||
|
TypeLiteral<T> typeLiteral,
|
||||||
|
EncounterImpl<T> encounter,
|
||||||
|
ImmutableList<SingleMemberInjector> memberInjectors) {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.typeLiteral = typeLiteral;
|
this.typeLiteral = typeLiteral;
|
||||||
this.memberInjectors = memberInjectors;
|
this.memberInjectors = memberInjectors.isEmpty() ? null : memberInjectors;
|
||||||
this.userMembersInjectors = encounter.getMembersInjectors();
|
this.userMembersInjectors =
|
||||||
this.injectionListeners = encounter.getInjectionListeners();
|
encounter.getMembersInjectors().isEmpty() ? null : encounter.getMembersInjectors().asList();
|
||||||
|
this.injectionListeners =
|
||||||
|
encounter.getInjectionListeners().isEmpty()
|
||||||
|
? null
|
||||||
|
: encounter.getInjectionListeners().asList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableList<SingleMemberInjector> getMemberInjectors() {
|
public ImmutableList<SingleMemberInjector> getMemberInjectors() {
|
||||||
return memberInjectors;
|
return memberInjectors == null ? ImmutableList.of() : memberInjectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void injectMembers(T instance) {
|
public void injectMembers(T instance) {
|
||||||
Errors errors = new Errors(typeLiteral);
|
TypeLiteral<T> localTypeLiteral = typeLiteral;
|
||||||
try {
|
try {
|
||||||
injectAndNotify(instance, errors, null, null, typeLiteral, false);
|
injectAndNotify(instance, null, null, localTypeLiteral, false);
|
||||||
} catch (ErrorsException e) {
|
} catch (InternalProvisionException ipe) {
|
||||||
errors.merge(e.getErrors());
|
throw ipe.addSource(localTypeLiteral).toProvisionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.throwProvisionExceptionIfErrorsExist();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void injectAndNotify(final T instance,
|
void injectAndNotify(final T instance,
|
||||||
final Errors errors,
|
|
||||||
final Key<T> key, // possibly null!
|
final Key<T> key, // possibly null!
|
||||||
final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
|
final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
|
||||||
final Object source,
|
final Object source,
|
||||||
final boolean toolableOnly) throws ErrorsException {
|
final boolean toolableOnly) throws InternalProvisionException {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final InternalContext context = injector.enterContext();
|
||||||
injector.callInContext(new ContextualCallable<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void call(final InternalContext context) throws ErrorsException {
|
|
||||||
context.pushState(key, source);
|
context.pushState(key, source);
|
||||||
try {
|
try {
|
||||||
if (provisionCallback != null && provisionCallback.hasListeners()) {
|
if (provisionCallback != null && provisionCallback.hasListeners()) {
|
||||||
provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
provisionCallback.provision(context, () -> {
|
||||||
@Override
|
injectMembers(instance, context, toolableOnly);
|
||||||
public T call() {
|
|
||||||
injectMembers(instance, errors, context, toolableOnly);
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
injectMembers(instance, errors, context, toolableOnly);
|
injectMembers(instance, context, toolableOnly);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
context.popState();
|
context.popState();
|
||||||
|
context.close();
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: We *could* notify listeners too here,
|
// TODO: We *could* notify listeners too here,
|
||||||
// but it's not clear if we want to. There's no way to know
|
// but it's not clear if we want to. There's no way to know
|
||||||
|
@ -85,38 +85,54 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
|
||||||
// the above callInContext could return 'true' if it injected
|
// the above callInContext could return 'true' if it injected
|
||||||
// anything.)
|
// anything.)
|
||||||
if (!toolableOnly) {
|
if (!toolableOnly) {
|
||||||
notifyListeners(instance, errors);
|
notifyListeners(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyListeners(T instance, Errors errors) throws ErrorsException {
|
void notifyListeners(T instance) throws InternalProvisionException {
|
||||||
int numErrorsBefore = errors.size();
|
ImmutableList<InjectionListener<? super T>> localInjectionListeners = injectionListeners;
|
||||||
for (InjectionListener<? super T> injectionListener : injectionListeners) {
|
if (localInjectionListeners == null) {
|
||||||
|
// no listeners
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// optimization: use manual for/each to save allocating an iterator here
|
||||||
|
for (int i = 0; i < localInjectionListeners.size(); i++) {
|
||||||
|
InjectionListener<? super T> injectionListener = localInjectionListeners.get(i);
|
||||||
try {
|
try {
|
||||||
injectionListener.afterInjection(instance);
|
injectionListener.afterInjection(instance);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
errors.errorNotifyingInjectionListener(injectionListener, typeLiteral, e);
|
throw InternalProvisionException.errorNotifyingInjectionListener(
|
||||||
|
injectionListener, typeLiteral, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errors.throwIfNewErrors(numErrorsBefore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void injectMembers(T t, Errors errors, InternalContext context, boolean toolableOnly) {
|
void injectMembers(T t, InternalContext context, boolean toolableOnly)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
ImmutableList<SingleMemberInjector> localMembersInjectors = memberInjectors;
|
||||||
|
if (localMembersInjectors != null) {
|
||||||
// optimization: use manual for/each to save allocating an iterator here
|
// optimization: use manual for/each to save allocating an iterator here
|
||||||
for (int i = 0, size = memberInjectors.size(); i < size; i++) {
|
for (int i = 0, size = localMembersInjectors.size(); i < size; i++) {
|
||||||
SingleMemberInjector injector = memberInjectors.get(i);
|
SingleMemberInjector injector = localMembersInjectors.get(i);
|
||||||
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
|
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
|
||||||
injector.inject(errors, context, t);
|
injector.inject(context, t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: There's no way to know if a user's MembersInjector wants toolable injections.
|
// TODO: There's no way to know if a user's MembersInjector wants toolable injections.
|
||||||
if (!toolableOnly) {
|
if (!toolableOnly) {
|
||||||
for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) {
|
ImmutableList<MembersInjector<? super T>> localUsersMembersInjectors = userMembersInjectors;
|
||||||
|
if (localUsersMembersInjectors != null) {
|
||||||
|
// optimization: use manual for/each to save allocating an iterator here
|
||||||
|
for (int i = 0; i < localUsersMembersInjectors.size(); i++) {
|
||||||
|
MembersInjector<? super T> userMembersInjector = localUsersMembersInjectors.get(i);
|
||||||
try {
|
try {
|
||||||
userMembersInjector.injectMembers(t);
|
userMembersInjector.injectMembers(t);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
errors.errorInUserInjector(userMembersInjector, typeLiteral, e);
|
throw InternalProvisionException.errorInUserInjector(
|
||||||
|
userMembersInjector, typeLiteral, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,11 +144,15 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableSet<InjectionPoint> getInjectionPoints() {
|
public ImmutableSet<InjectionPoint> getInjectionPoints() {
|
||||||
|
ImmutableList<SingleMemberInjector> localMemberInjectors = memberInjectors;
|
||||||
|
if (localMemberInjectors != null) {
|
||||||
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
for (SingleMemberInjector memberInjector : localMemberInjectors) {
|
||||||
builder.add(memberInjector.getInjectionPoint());
|
builder.add(memberInjector.getInjectionPoint());
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
return ImmutableSet.of();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.inject.ConfigurationException;
|
import com.google.inject.ConfigurationException;
|
||||||
|
@ -11,24 +14,25 @@ import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Members injectors by type.
|
* Members injectors by type.
|
||||||
*/
|
*/
|
||||||
final class MembersInjectorStore {
|
final class MembersInjectorStore {
|
||||||
|
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private final ImmutableList<TypeListenerBinding> typeListenerBindings;
|
private final ImmutableList<TypeListenerBinding> typeListenerBindings;
|
||||||
|
|
||||||
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache
|
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache = new FailableCache<>() {
|
||||||
= new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() {
|
|
||||||
@Override
|
@Override
|
||||||
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
|
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
return createWithListeners(type, errors);
|
return createWithListeners(type, errors);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MembersInjectorStore(InjectorImpl injector,
|
MembersInjectorStore(InjectorImpl injector,
|
||||||
List<TypeListenerBinding> typeListenerBindings) {
|
List<TypeListenerBinding> typeListenerBindings) {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
|
@ -39,7 +43,7 @@ final class MembersInjectorStore {
|
||||||
* Returns true if any type listeners are installed. Other code may take shortcuts when there
|
* Returns true if any type listeners are installed. Other code may take shortcuts when there
|
||||||
* aren't any type listeners.
|
* aren't any type listeners.
|
||||||
*/
|
*/
|
||||||
public boolean hasTypeListeners() {
|
boolean hasTypeListeners() {
|
||||||
return !typeListenerBindings.isEmpty();
|
return !typeListenerBindings.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +85,7 @@ final class MembersInjectorStore {
|
||||||
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
|
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
|
||||||
errors.throwIfNewErrors(numErrorsBefore);
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
|
||||||
EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups);
|
EncounterImpl<T> encounter = new EncounterImpl<>(errors, injector.lookups);
|
||||||
Set<TypeListener> alreadySeenListeners = Sets.newHashSet();
|
Set<TypeListener> alreadySeenListeners = Sets.newHashSet();
|
||||||
for (TypeListenerBinding binding : typeListenerBindings) {
|
for (TypeListenerBinding binding : typeListenerBindings) {
|
||||||
TypeListener typeListener = binding.getListener();
|
TypeListener typeListener = binding.getListener();
|
||||||
|
@ -97,7 +101,7 @@ final class MembersInjectorStore {
|
||||||
encounter.invalidate();
|
encounter.invalidate();
|
||||||
errors.throwIfNewErrors(numErrorsBefore);
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
|
||||||
return new MembersInjectorImpl<T>(injector, type, encounter, injectors);
|
return new MembersInjectorImpl<>(injector, type, encounter, injectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,4 +125,11 @@ final class MembersInjectorStore {
|
||||||
}
|
}
|
||||||
return ImmutableList.copyOf(injectors);
|
return ImmutableList.copyOf(injectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImmutableListMultimap<TypeLiteral<?>, InjectionPoint> getAllInjectionPoints() {
|
||||||
|
return cache.asMap().entrySet().stream()
|
||||||
|
.collect(
|
||||||
|
flatteningToImmutableListMultimap(
|
||||||
|
Map.Entry::getKey, entry -> entry.getValue().getInjectionPoints().stream()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,20 +12,8 @@ final class MessageProcessor extends AbstractProcessor {
|
||||||
super(errors);
|
super(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getRootMessage(Throwable t) {
|
|
||||||
Throwable cause = t.getCause();
|
|
||||||
return cause == null ? t.toString() : getRootMessage(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean visit(Message message) {
|
public Boolean visit(Message message) {
|
||||||
if (message.getCause() != null) {
|
|
||||||
String rootMessage = getRootMessage(message.getCause());
|
|
||||||
/*logger.log(Level.INFO,
|
|
||||||
"An exception was caught and reported. Message: " + rootMessage,
|
|
||||||
message.getCause());*/
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.addMessage(message);
|
errors.addMessage(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
391
src/main/java/com/google/inject/internal/Messages.java
Normal file
391
src/main/java/com/google/inject/internal/Messages.java
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import com.google.common.base.Equivalence;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.util.Classes;
|
||||||
|
import com.google.inject.internal.util.StackTraceElements;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.ElementSource;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for {@link Message} objects
|
||||||
|
*/
|
||||||
|
public final class Messages {
|
||||||
|
|
||||||
|
private Messages() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends the list of sources to the given {@link Message}
|
||||||
|
*/
|
||||||
|
static Message mergeSources(List<Object> sources, Message message) {
|
||||||
|
List<Object> messageSources = message.getSources();
|
||||||
|
// It is possible that the end of getSources() and the beginning of message.getSources() are
|
||||||
|
// equivalent, in this case we should drop the repeated source when joining the lists. The
|
||||||
|
// most likely scenario where this would happen is when a scoped binding throws an exception,
|
||||||
|
// due to the fact that InternalFactoryToProviderAdapter applies the binding source when
|
||||||
|
// merging errors.
|
||||||
|
if (!sources.isEmpty() && !messageSources.isEmpty() &&
|
||||||
|
Objects.equal(messageSources.get(0), sources.get(sources.size() - 1))) {
|
||||||
|
messageSources = messageSources.subList(1, messageSources.size());
|
||||||
|
}
|
||||||
|
return new Message(ImmutableList.builder().addAll(sources).addAll(messageSources).build(),
|
||||||
|
message.getMessage(), message.getCause());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link String#format} after converting the arguments using some standard guice formatting
|
||||||
|
* for {@link Key}, {@link Class} and {@link Member} objects.
|
||||||
|
*/
|
||||||
|
public static String format(String messageFormat, Object... arguments) {
|
||||||
|
for (int i = 0; i < arguments.length; i++) {
|
||||||
|
arguments[i] = convert(arguments[i]);
|
||||||
|
}
|
||||||
|
return String.format(messageFormat, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the formatted message for an exception with the specified messages.
|
||||||
|
*/
|
||||||
|
public static String formatMessages(String heading, Collection<Message> errorMessages) {
|
||||||
|
Formatter fmt = new Formatter().format(heading).format(":%n%n");
|
||||||
|
int index = 1;
|
||||||
|
boolean displayCauses = getOnlyCause(errorMessages) == null;
|
||||||
|
|
||||||
|
Map<Equivalence.Wrapper<Throwable>, Integer> causes = Maps.newHashMap();
|
||||||
|
for (Message errorMessage : errorMessages) {
|
||||||
|
int thisIdx = index++;
|
||||||
|
fmt.format("%s) %s%n", thisIdx, errorMessage.getMessage());
|
||||||
|
|
||||||
|
List<Object> dependencies = errorMessage.getSources();
|
||||||
|
for (int i = dependencies.size() - 1; i >= 0; i--) {
|
||||||
|
Object source = dependencies.get(i);
|
||||||
|
formatSource(fmt, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
Throwable cause = errorMessage.getCause();
|
||||||
|
if (displayCauses && cause != null) {
|
||||||
|
Equivalence.Wrapper<Throwable> causeEquivalence = ThrowableEquivalence.INSTANCE.wrap(cause);
|
||||||
|
if (!causes.containsKey(causeEquivalence)) {
|
||||||
|
causes.put(causeEquivalence, thisIdx);
|
||||||
|
fmt.format("Caused by: %s", Throwables.getStackTraceAsString(cause));
|
||||||
|
} else {
|
||||||
|
int causeIdx = causes.get(causeEquivalence);
|
||||||
|
fmt.format(
|
||||||
|
"Caused by: %s (same stack trace as error #%s)",
|
||||||
|
cause.getClass().getName(), causeIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.format("%n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessages.size() == 1) {
|
||||||
|
fmt.format("1 error");
|
||||||
|
} else {
|
||||||
|
fmt.format("%s errors", errorMessages.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Message without a cause.
|
||||||
|
*
|
||||||
|
* @param messageFormat Format string
|
||||||
|
* @param arguments format string arguments
|
||||||
|
*/
|
||||||
|
public static Message create(String messageFormat, Object... arguments) {
|
||||||
|
return create(null, messageFormat, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Message with the given cause.
|
||||||
|
*
|
||||||
|
* @param cause The exception that caused the error
|
||||||
|
* @param messageFormat Format string
|
||||||
|
* @param arguments format string arguments
|
||||||
|
*/
|
||||||
|
public static Message create(Throwable cause, String messageFormat, Object... arguments) {
|
||||||
|
return create(cause, ImmutableList.of(), messageFormat, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Message with the given cause and a binding source stack.
|
||||||
|
*
|
||||||
|
* @param cause The exception that caused the error
|
||||||
|
* @param sources The binding sources for the source stack
|
||||||
|
* @param messageFormat Format string
|
||||||
|
* @param arguments format string arguments
|
||||||
|
*/
|
||||||
|
public static Message create(Throwable cause, List<Object> sources, String messageFormat, Object... arguments) {
|
||||||
|
String message = format(messageFormat, arguments);
|
||||||
|
return new Message(sources, message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats an object in a user friendly way.
|
||||||
|
*/
|
||||||
|
public static Object convert(Object o) {
|
||||||
|
ElementSource source = null;
|
||||||
|
if (o instanceof ElementSource) {
|
||||||
|
source = (ElementSource) o;
|
||||||
|
o = source.getDeclaringSource();
|
||||||
|
}
|
||||||
|
return convert(o, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object convert(Object o, ElementSource source) {
|
||||||
|
for (Converter<?> converter : converters) {
|
||||||
|
if (converter.appliesTo(o)) {
|
||||||
|
return appendModules(converter.convert(o), source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appendModules(o, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object appendModules(Object source, ElementSource elementSource) {
|
||||||
|
String modules = moduleSourceString(elementSource);
|
||||||
|
if (modules.length() == 0) {
|
||||||
|
return source;
|
||||||
|
} else {
|
||||||
|
return source + modules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String moduleSourceString(ElementSource elementSource) {
|
||||||
|
// if we only have one module (or don't know what they are), then don't bother
|
||||||
|
// reporting it, because the source already is going to report exactly that module.
|
||||||
|
if (elementSource == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
|
||||||
|
// Insert any original element sources w/ module info into the path.
|
||||||
|
while (elementSource.getOriginalElementSource() != null) {
|
||||||
|
elementSource = elementSource.getOriginalElementSource();
|
||||||
|
modules.addAll(0, elementSource.getModuleClassNames());
|
||||||
|
}
|
||||||
|
if (modules.size() <= 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ideally we'd do:
|
||||||
|
// return Joiner.on(" -> ")
|
||||||
|
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
|
||||||
|
// .append(")").toString();
|
||||||
|
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
|
||||||
|
StringBuilder builder = new StringBuilder(" (via modules: ");
|
||||||
|
for (int i = modules.size() - 1; i >= 0; i--) {
|
||||||
|
builder.append(modules.get(i));
|
||||||
|
if (i != 0) {
|
||||||
|
builder.append(" -> ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append(")");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void formatSource(Formatter formatter, Object source) {
|
||||||
|
ElementSource elementSource = null;
|
||||||
|
if (source instanceof ElementSource) {
|
||||||
|
elementSource = (ElementSource) source;
|
||||||
|
source = elementSource.getDeclaringSource();
|
||||||
|
}
|
||||||
|
formatSource(formatter, source, elementSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
|
||||||
|
String modules = moduleSourceString(elementSource);
|
||||||
|
if (source instanceof Dependency) {
|
||||||
|
Dependency<?> dependency = (Dependency<?>) source;
|
||||||
|
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
||||||
|
if (injectionPoint != null) {
|
||||||
|
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
|
||||||
|
} else {
|
||||||
|
formatSource(formatter, dependency.getKey(), elementSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (source instanceof InjectionPoint) {
|
||||||
|
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
|
||||||
|
|
||||||
|
} else if (source instanceof Class) {
|
||||||
|
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
|
||||||
|
|
||||||
|
} else if (source instanceof Member) {
|
||||||
|
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
|
||||||
|
|
||||||
|
} else if (source instanceof TypeLiteral) {
|
||||||
|
formatter.format(" while locating %s%s%n", source, modules);
|
||||||
|
|
||||||
|
} else if (source instanceof Key) {
|
||||||
|
Key<?> key = (Key<?>) source;
|
||||||
|
formatter.format(" while locating %s%n", convert(key, elementSource));
|
||||||
|
|
||||||
|
} else if (source instanceof Thread) {
|
||||||
|
formatter.format(" in thread %s%n", source);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
formatter.format(" at %s%s%n", source, modules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void formatInjectionPoint(
|
||||||
|
Formatter formatter,
|
||||||
|
Dependency<?> dependency,
|
||||||
|
InjectionPoint injectionPoint,
|
||||||
|
ElementSource elementSource) {
|
||||||
|
Member member = injectionPoint.getMember();
|
||||||
|
Class<? extends Member> memberType = Classes.memberType(member);
|
||||||
|
|
||||||
|
if (memberType == Field.class) {
|
||||||
|
dependency = injectionPoint.getDependencies().get(0);
|
||||||
|
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
|
||||||
|
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
|
||||||
|
|
||||||
|
} else if (dependency != null) {
|
||||||
|
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
|
||||||
|
formatter.format(" for %s%n", formatParameter(dependency));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
formatSource(formatter, injectionPoint.getMember());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String formatParameter(Dependency<?> dependency) {
|
||||||
|
int ordinal = dependency.getParameterIndex() + 1;
|
||||||
|
return String.format(
|
||||||
|
"the %s%s parameter of %s",
|
||||||
|
ordinal,
|
||||||
|
getOrdinalSuffix(ordinal),
|
||||||
|
StackTraceElements.forMember(dependency.getInjectionPoint().getMember()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps {@code 1} to the string {@code "1st"} ditto for all non-negative numbers
|
||||||
|
*
|
||||||
|
* @see <a href="https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers">
|
||||||
|
* https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers</a>
|
||||||
|
*/
|
||||||
|
private static String getOrdinalSuffix(int ordinal) {
|
||||||
|
// negative ordinals don't make sense, we allow zero though because we are programmers
|
||||||
|
checkArgument(ordinal >= 0);
|
||||||
|
if ((ordinal / 10) % 10 == 1) {
|
||||||
|
// all the 'teens' are weird
|
||||||
|
return "th";
|
||||||
|
} else {
|
||||||
|
// could use a lookup table? any better?
|
||||||
|
switch (ordinal % 10) {
|
||||||
|
case 1:
|
||||||
|
return "st";
|
||||||
|
case 2:
|
||||||
|
return "nd";
|
||||||
|
case 3:
|
||||||
|
return "rd";
|
||||||
|
default:
|
||||||
|
return "th";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class Converter<T> {
|
||||||
|
|
||||||
|
final Class<T> type;
|
||||||
|
|
||||||
|
Converter(Class<T> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean appliesTo(Object o) {
|
||||||
|
return o != null && type.isAssignableFrom(o.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
String convert(Object o) {
|
||||||
|
return toString(type.cast(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract String toString(T t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes"}) // rawtypes aren't avoidable
|
||||||
|
private static final Collection<Converter<?>> converters =
|
||||||
|
ImmutableList.of(
|
||||||
|
new Converter<>(Class.class) {
|
||||||
|
@Override
|
||||||
|
public String toString(Class c) {
|
||||||
|
return c.getName();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Converter<>(Member.class) {
|
||||||
|
@Override
|
||||||
|
public String toString(Member member) {
|
||||||
|
return Classes.toString(member);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Converter<>(Key.class) {
|
||||||
|
@Override
|
||||||
|
public String toString(Key key) {
|
||||||
|
if (key.getAnnotationType() != null) {
|
||||||
|
return key.getTypeLiteral()
|
||||||
|
+ " annotated with "
|
||||||
|
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
|
||||||
|
} else {
|
||||||
|
return key.getTypeLiteral().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
|
||||||
|
* zero or multiple messages with causes, null is returned.
|
||||||
|
*/
|
||||||
|
public static Throwable getOnlyCause(Collection<Message> messages) {
|
||||||
|
Throwable onlyCause = null;
|
||||||
|
for (Message message : messages) {
|
||||||
|
Throwable messageCause = message.getCause();
|
||||||
|
if (messageCause == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlyCause != null && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyCause = messageCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
return onlyCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ThrowableEquivalence extends Equivalence<Throwable> {
|
||||||
|
static final ThrowableEquivalence INSTANCE = new ThrowableEquivalence();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doEquivalent(Throwable a, Throwable b) {
|
||||||
|
return a.getClass().equals(b.getClass())
|
||||||
|
&& Objects.equal(a.getMessage(), b.getMessage())
|
||||||
|
&& Arrays.equals(a.getStackTrace(), b.getStackTrace())
|
||||||
|
&& equivalent(a.getCause(), b.getCause());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doHash(Throwable t) {
|
||||||
|
return Objects.hashCode(t.getClass().hashCode(), t.getMessage(), hash(t.getCause()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -264,7 +264,7 @@ public class MoreTypes {
|
||||||
|
|
||||||
// we skip searching through interfaces if unknown is an interface
|
// we skip searching through interfaces if unknown is an interface
|
||||||
if (toResolve.isInterface()) {
|
if (toResolve.isInterface()) {
|
||||||
Class[] interfaces = rawType.getInterfaces();
|
Class<?>[] interfaces = rawType.getInterfaces();
|
||||||
for (int i = 0, length = interfaces.length; i < length; i++) {
|
for (int i = 0, length = interfaces.length; i < length; i++) {
|
||||||
if (interfaces[i] == toResolve) {
|
if (interfaces[i] == toResolve) {
|
||||||
return rawType.getGenericInterfaces()[i];
|
return rawType.getGenericInterfaces()[i];
|
||||||
|
@ -291,7 +291,7 @@ public class MoreTypes {
|
||||||
return toResolve;
|
return toResolve;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) {
|
public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable<?> unknown) {
|
||||||
Class<?> declaredByRaw = declaringClassOf(unknown);
|
Class<?> declaredByRaw = declaringClassOf(unknown);
|
||||||
|
|
||||||
// we can't reduce this further
|
// we can't reduce this further
|
||||||
|
@ -321,7 +321,7 @@ public class MoreTypes {
|
||||||
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
|
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
|
||||||
* a class.
|
* a class.
|
||||||
*/
|
*/
|
||||||
private static Class<?> declaringClassOf(TypeVariable typeVariable) {
|
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
|
||||||
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
|
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
|
||||||
return genericDeclaration instanceof Class
|
return genericDeclaration instanceof Class
|
||||||
? (Class<?>) genericDeclaration
|
? (Class<?>) genericDeclaration
|
||||||
|
@ -365,7 +365,7 @@ public class MoreTypes {
|
||||||
|
|
||||||
private static void ensureOwnerType(Type ownerType, Type rawType) {
|
private static void ensureOwnerType(Type ownerType, Type rawType) {
|
||||||
if (rawType instanceof Class<?>) {
|
if (rawType instanceof Class<?>) {
|
||||||
Class rawTypeAsClass = (Class) rawType;
|
Class<?> rawTypeAsClass = (Class) rawType;
|
||||||
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
|
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
|
||||||
"No owner type for enclosed %s", rawType);
|
"No owner type for enclosed %s", rawType);
|
||||||
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
|
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
|
||||||
|
@ -373,18 +373,22 @@ public class MoreTypes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Type[] getActualTypeArguments() {
|
public Type[] getActualTypeArguments() {
|
||||||
return typeArguments.clone();
|
return typeArguments.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Type getRawType() {
|
public Type getRawType() {
|
||||||
return rawType;
|
return rawType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Type getOwnerType() {
|
public Type getOwnerType() {
|
||||||
return ownerType;
|
return ownerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isFullySpecified() {
|
public boolean isFullySpecified() {
|
||||||
if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
|
if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -441,10 +445,12 @@ public class MoreTypes {
|
||||||
this.componentType = canonicalize(componentType);
|
this.componentType = canonicalize(componentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Type getGenericComponentType() {
|
public Type getGenericComponentType() {
|
||||||
return componentType;
|
return componentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isFullySpecified() {
|
public boolean isFullySpecified() {
|
||||||
return MoreTypes.isFullySpecified(componentType);
|
return MoreTypes.isFullySpecified(componentType);
|
||||||
}
|
}
|
||||||
|
@ -495,14 +501,17 @@ public class MoreTypes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Type[] getUpperBounds() {
|
public Type[] getUpperBounds() {
|
||||||
return new Type[]{upperBound};
|
return new Type[]{upperBound};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Type[] getLowerBounds() {
|
public Type[] getLowerBounds() {
|
||||||
return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
|
return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isFullySpecified() {
|
public boolean isFullySpecified() {
|
||||||
return MoreTypes.isFullySpecified(upperBound)
|
return MoreTypes.isFullySpecified(upperBound)
|
||||||
&& (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
|
&& (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
|
||||||
|
|
|
@ -25,7 +25,7 @@ final class PrivateElementProcessor extends AbstractProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<InjectorShell.Builder> getInjectorShellBuilders() {
|
List<InjectorShell.Builder> getInjectorShellBuilders() {
|
||||||
return injectorShellBuilders;
|
return injectorShellBuilders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,12 @@ public final class PrivateElementsImpl implements PrivateElements {
|
||||||
this.source = checkNotNull(source, "source");
|
this.source = checkNotNull(source, "source");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getSource() {
|
public Object getSource() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Element> getElements() {
|
public List<Element> getElements() {
|
||||||
if (elements == null) {
|
if (elements == null) {
|
||||||
elements = ImmutableList.copyOf(elementsMutable);
|
elements = ImmutableList.copyOf(elementsMutable);
|
||||||
|
@ -62,15 +64,17 @@ public final class PrivateElementsImpl implements PrivateElements {
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector getInjector() {
|
public Injector getInjector() {
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initInjector(Injector injector) {
|
void initInjector(Injector injector) {
|
||||||
checkState(this.injector == null, "injector already initialized");
|
checkState(this.injector == null, "injector already initialized");
|
||||||
this.injector = checkNotNull(injector, "injector");
|
this.injector = checkNotNull(injector, "injector");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Key<?>> getExposedKeys() {
|
public Set<Key<?>> getExposedKeys() {
|
||||||
if (exposedKeysToSources == null) {
|
if (exposedKeysToSources == null) {
|
||||||
Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap();
|
Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap();
|
||||||
|
@ -84,6 +88,7 @@ public final class PrivateElementsImpl implements PrivateElements {
|
||||||
return exposedKeysToSources.keySet();
|
return exposedKeysToSources.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
|
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
@ -96,6 +101,7 @@ public final class PrivateElementsImpl implements PrivateElements {
|
||||||
exposureBuilders.add(exposureBuilder);
|
exposureBuilders.add(exposureBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder();
|
PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder();
|
||||||
|
|
||||||
|
@ -109,6 +115,7 @@ public final class PrivateElementsImpl implements PrivateElements {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getExposedSource(Key<?> key) {
|
public Object getExposedSource(Key<?> key) {
|
||||||
getExposedKeys(); // ensure exposedKeysToSources is populated
|
getExposedKeys(); // ensure exposedKeysToSources is populated
|
||||||
Object source = exposedKeysToSources.get(key);
|
Object source = exposedKeysToSources.get(key);
|
||||||
|
|
|
@ -11,8 +11,11 @@ import java.util.List;
|
||||||
class ProcessedBindingData {
|
class ProcessedBindingData {
|
||||||
|
|
||||||
private final List<CreationListener> creationListeners = Lists.newArrayList();
|
private final List<CreationListener> creationListeners = Lists.newArrayList();
|
||||||
|
|
||||||
private final List<Runnable> uninitializedBindings = Lists.newArrayList();
|
private final List<Runnable> uninitializedBindings = Lists.newArrayList();
|
||||||
|
|
||||||
|
private final List<Runnable> delayedUninitializedBindings = Lists.newArrayList();
|
||||||
|
|
||||||
void addCreationListener(CreationListener listener) {
|
void addCreationListener(CreationListener listener) {
|
||||||
creationListeners.add(listener);
|
creationListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +24,10 @@ class ProcessedBindingData {
|
||||||
uninitializedBindings.add(runnable);
|
uninitializedBindings.add(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addDelayedUninitializedBinding(Runnable runnable) {
|
||||||
|
delayedUninitializedBindings.add(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
void initializeBindings() {
|
void initializeBindings() {
|
||||||
for (Runnable initializer : uninitializedBindings) {
|
for (Runnable initializer : uninitializedBindings) {
|
||||||
initializer.run();
|
initializer.run();
|
||||||
|
@ -33,4 +40,14 @@ class ProcessedBindingData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialized bindings that need to be delayed until after all injection points and other
|
||||||
|
* bindings are processed. The main current usecase for this is resolving Optional dependencies
|
||||||
|
* for OptionalBinder bindings.
|
||||||
|
*/
|
||||||
|
void initializeDelayedBindings() {
|
||||||
|
for (Runnable initializer : delayedUninitializedBindings) {
|
||||||
|
initializer.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,9 @@ package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.ProvidedBy;
|
import com.google.inject.ProvidedBy;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.internal.InjectorImpl.JitLimitation;
|
import com.google.inject.internal.InjectorImpl.JitLimitation;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
|
import javax.inject.Provider;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings.
|
* An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings.
|
||||||
|
@ -34,40 +32,48 @@ class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
|
||||||
provisionCallback = listener;
|
provisionCallback = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
providerBinding =
|
providerBinding =
|
||||||
injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
|
injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
checkState(providerBinding != null, "not initialized");
|
throws InternalProvisionException {
|
||||||
|
BindingImpl<? extends Provider<T>> localProviderBinding = providerBinding;
|
||||||
|
if (localProviderBinding == null) {
|
||||||
|
throw new IllegalStateException("not initialized");
|
||||||
|
}
|
||||||
|
Key<? extends Provider<T>> localProviderKey = providerKey;
|
||||||
|
context.pushState(localProviderKey, localProviderBinding.getSource());
|
||||||
|
|
||||||
context.pushState(providerKey, providerBinding.getSource());
|
|
||||||
try {
|
try {
|
||||||
errors = errors.withSource(providerKey);
|
Provider<? extends T> provider =
|
||||||
Provider<? extends T> provider = providerBinding.getInternalFactory().get(
|
localProviderBinding.getInternalFactory().get(context, dependency, true);
|
||||||
errors, context, dependency, true);
|
return circularGet(provider, context, dependency, provisionCallback);
|
||||||
return circularGet(provider, errors, context, dependency, provisionCallback);
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(localProviderKey);
|
||||||
} finally {
|
} finally {
|
||||||
context.popState();
|
context.popState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
|
protected T provision(javax.inject.Provider<? extends T> provider,
|
||||||
Dependency<?> dependency, ConstructionContext<T> constructionContext)
|
Dependency<?> dependency,
|
||||||
throws ErrorsException {
|
ConstructionContext<T> constructionContext)
|
||||||
|
throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
Object o = super.provision(provider, errors, dependency, constructionContext);
|
Object o = super.provision(provider, dependency, constructionContext);
|
||||||
if (o != null && !rawType.isInstance(o)) {
|
if (o != null && !rawType.isInstance(o)) {
|
||||||
throw errors.subtypeNotProvided(providerType, rawType).toException();
|
throw InternalProvisionException.subtypeNotProvided(providerType, rawType);
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked") // protected by isInstance() check above
|
@SuppressWarnings("unchecked") // protected by isInstance() check above
|
||||||
T t = (T) o;
|
T t = (T) o;
|
||||||
return t;
|
return t;
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
throw errors.errorInProvider(e).toException();
|
throw InternalProvisionException.errorInProvider(e).addSource(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,13 @@ import com.google.inject.spi.ProviderWithExtensionVisitor;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
|
class ProviderInstanceBindingImpl<T> extends BindingImpl<T> implements ProviderInstanceBinding<T> {
|
||||||
implements ProviderInstanceBinding<T> {
|
|
||||||
|
|
||||||
final javax.inject.Provider<? extends T> providerInstance;
|
private final javax.inject.Provider<? extends T> providerInstance;
|
||||||
final ImmutableSet<InjectionPoint> injectionPoints;
|
|
||||||
|
|
||||||
public ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
|
private final ImmutableSet<InjectionPoint> injectionPoints;
|
||||||
|
|
||||||
|
ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
|
||||||
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
|
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
javax.inject.Provider<? extends T> providerInstance,
|
javax.inject.Provider<? extends T> providerInstance,
|
||||||
Set<InjectionPoint> injectionPoints) {
|
Set<InjectionPoint> injectionPoints) {
|
||||||
|
@ -29,7 +29,7 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
|
||||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProviderInstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
ProviderInstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
||||||
Set<InjectionPoint> injectionPoints, javax.inject.Provider<? extends T> providerInstance) {
|
Set<InjectionPoint> injectionPoints, javax.inject.Provider<? extends T> providerInstance) {
|
||||||
super(source, key, scoping);
|
super(source, key, scoping);
|
||||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
|
@ -61,13 +61,11 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
return new ProviderInstanceBindingImpl<T>(
|
return new ProviderInstanceBindingImpl<>(getSource(), getKey(), scoping, injectionPoints, providerInstance);
|
||||||
getSource(), getKey(), scoping, injectionPoints, providerInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BindingImpl<T> withKey(Key<T> key) {
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
return new ProviderInstanceBindingImpl<T>(
|
return new ProviderInstanceBindingImpl<>(getSource(), key, getScoping(), injectionPoints, providerInstance);
|
||||||
getSource(), key, getScoping(), injectionPoints, providerInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
@ -20,33 +19,28 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
|
||||||
this.source = checkNotNull(source, "source");
|
this.source = checkNotNull(source, "source");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected T circularGet(final Provider<? extends T> provider, final Errors errors,
|
protected T circularGet(final Provider<? extends T> provider,
|
||||||
InternalContext context, final Dependency<?> dependency,
|
InternalContext context,
|
||||||
|
final Dependency<?> dependency,
|
||||||
ProvisionListenerStackCallback<T> provisionCallback)
|
ProvisionListenerStackCallback<T> provisionCallback)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||||
|
|
||||||
// We have a circular reference between constructors. Return a proxy.
|
// We have a circular reference between constructors. Return a proxy.
|
||||||
if (constructionContext.isConstructing()) {
|
if (constructionContext.isConstructing()) {
|
||||||
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
|
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
|
||||||
// TODO: if we can't proxy this object, can we proxy the other object?
|
// TODO: if we can't proxy this object, can we proxy the other object?
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
T proxyType = (T) constructionContext.createProxy(
|
T proxyType = (T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
|
||||||
errors, context.getInjectorOptions(), expectedType);
|
|
||||||
return proxyType;
|
return proxyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: Don't go through the callback stack if no one's listening.
|
// Optimization: Don't go through the callback stack if no one's listening.
|
||||||
constructionContext.startConstruction();
|
constructionContext.startConstruction();
|
||||||
try {
|
try {
|
||||||
if (!provisionCallback.hasListeners()) {
|
if (!provisionCallback.hasListeners()) {
|
||||||
return provision(provider, errors, dependency, constructionContext);
|
return provision(provider, dependency, constructionContext);
|
||||||
} else {
|
} else {
|
||||||
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
return provisionCallback.provision(context, () ->
|
||||||
public T call() throws ErrorsException {
|
provision(provider, dependency, constructionContext));
|
||||||
return provision(provider, errors, dependency, constructionContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
constructionContext.removeCurrentReference();
|
constructionContext.removeCurrentReference();
|
||||||
|
@ -58,9 +52,12 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
|
||||||
* Provisions a new instance. Subclasses should override this to catch
|
* Provisions a new instance. Subclasses should override this to catch
|
||||||
* exceptions & rethrow as ErrorsExceptions.
|
* exceptions & rethrow as ErrorsExceptions.
|
||||||
*/
|
*/
|
||||||
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
|
||||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||||
T t = errors.checkForNull(provider.get(), source, dependency);
|
T t = provider.get();
|
||||||
|
if (t == null && !dependency.isNullable()) {
|
||||||
|
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
|
||||||
|
}
|
||||||
constructionContext.setProxyDelegates(t);
|
constructionContext.setProxyDelegates(t);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import com.google.inject.Binder;
|
||||||
import com.google.inject.Exposed;
|
import com.google.inject.Exposed;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.PrivateBinder;
|
import com.google.inject.PrivateBinder;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.internal.util.StackTraceElements;
|
import com.google.inject.internal.util.StackTraceElements;
|
||||||
import com.google.inject.spi.BindingTargetVisitor;
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
@ -21,36 +20,50 @@ import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A provider that invokes a method and returns its result.
|
* A provider that invokes a method and returns its result.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
|
public abstract class ProviderMethod<T>
|
||||||
ProvidesMethodBinding<T> {
|
extends InternalProviderInstanceBindingImpl.CyclicFactory<T>
|
||||||
|
implements HasDependencies, ProviderWithExtensionVisitor<T>, ProvidesMethodBinding<T> {
|
||||||
|
|
||||||
protected final Object instance;
|
protected final Object instance;
|
||||||
|
|
||||||
protected final Method method;
|
protected final Method method;
|
||||||
|
|
||||||
private final Key<T> key;
|
private final Key<T> key;
|
||||||
|
|
||||||
private final Class<? extends Annotation> scopeAnnotation;
|
private final Class<? extends Annotation> scopeAnnotation;
|
||||||
|
|
||||||
private final ImmutableSet<Dependency<?>> dependencies;
|
private final ImmutableSet<Dependency<?>> dependencies;
|
||||||
private final List<Provider<?>> parameterProviders;
|
|
||||||
private final boolean exposed;
|
private final boolean exposed;
|
||||||
|
|
||||||
private final Annotation annotation;
|
private final Annotation annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set by {@link #initialize(InjectorImpl, Errors)} so it is always available prior to injection.
|
||||||
|
*/
|
||||||
|
private SingleParameterInjector<?>[] parameterInjectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param method the method to invoke. It's return type must be the same type as {@code key}.
|
* @param method the method to invoke. It's return type must be the same type as {@code key}.
|
||||||
*/
|
*/
|
||||||
private ProviderMethod(Key<T> key, Method method, Object instance,
|
private ProviderMethod(Key<T> key,
|
||||||
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
|
Method method,
|
||||||
Class<? extends Annotation> scopeAnnotation, Annotation annotation) {
|
Object instance,
|
||||||
|
ImmutableSet<Dependency<?>> dependencies,
|
||||||
|
Class<? extends Annotation> scopeAnnotation,
|
||||||
|
Annotation annotation) {
|
||||||
|
super(InternalProviderInstanceBindingImpl.InitializationTiming.EAGER);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.scopeAnnotation = scopeAnnotation;
|
this.scopeAnnotation = scopeAnnotation;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.dependencies = dependencies;
|
this.dependencies = dependencies;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.parameterProviders = parameterProviders;
|
|
||||||
this.exposed = method.isAnnotationPresent(Exposed.class);
|
this.exposed = method.isAnnotationPresent(Exposed.class);
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
}
|
}
|
||||||
|
@ -58,24 +71,19 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
|
||||||
/**
|
/**
|
||||||
* Creates a {@link ProviderMethod}.
|
* Creates a {@link ProviderMethod}.
|
||||||
*/
|
*/
|
||||||
static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance,
|
static <T> ProviderMethod<T> create(Key<T> key,
|
||||||
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
|
Method method,
|
||||||
Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration,
|
Object instance,
|
||||||
|
ImmutableSet<Dependency<?>> dependencies,
|
||||||
|
Class<? extends Annotation> scopeAnnotation,
|
||||||
|
boolean skipFastClassGeneration,
|
||||||
Annotation annotation) {
|
Annotation annotation) {
|
||||||
int modifiers = method.getModifiers();
|
int modifiers = method.getModifiers();
|
||||||
|
|
||||||
if (!Modifier.isPublic(modifiers) ||
|
if (!Modifier.isPublic(modifiers) ||
|
||||||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
return new ReflectionProviderMethod<>(key, method, instance, dependencies, scopeAnnotation, annotation);
|
||||||
return new ReflectionProviderMethod<T>(key,
|
|
||||||
method,
|
|
||||||
instance,
|
|
||||||
dependencies,
|
|
||||||
parameterProviders,
|
|
||||||
scopeAnnotation,
|
|
||||||
annotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -120,27 +128,31 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T get() {
|
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
Object[] parameters = new Object[parameterProviders.size()];
|
parameterInjectors = injector.getParametersInjectors(dependencies.asList(), errors);
|
||||||
for (int i = 0; i < parameters.length; i++) {
|
|
||||||
parameters[i] = parameterProviders.get(i).get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T doProvision(InternalContext context, Dependency<?> dependency)
|
||||||
|
throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
T t = doProvision(SingleParameterInjector.getAll(context, parameterInjectors));
|
||||||
T result = (T) doProvision(parameters);
|
if (t == null && !dependency.isNullable()) {
|
||||||
return result;
|
InternalProvisionException.onNullInjectedIntoNonNullableDependency(getMethod(), dependency);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException userException) {
|
||||||
throw Exceptions.rethrowCause(e);
|
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
|
||||||
|
throw InternalProvisionException.errorInProvider(cause).addSource(getSource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension point for our subclasses to implement the provisioning strategy.
|
* Extension point for our subclasses to implement the provisioning strategy.
|
||||||
*/
|
*/
|
||||||
abstract Object doProvision(Object[] parameters)
|
abstract T doProvision(Object[] parameters)
|
||||||
throws IllegalAccessException, InvocationTargetException;
|
throws IllegalAccessException, InvocationTargetException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -199,22 +211,21 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
|
||||||
Method method,
|
Method method,
|
||||||
Object instance,
|
Object instance,
|
||||||
ImmutableSet<Dependency<?>> dependencies,
|
ImmutableSet<Dependency<?>> dependencies,
|
||||||
List<Provider<?>> parameterProviders,
|
|
||||||
Class<? extends Annotation> scopeAnnotation,
|
Class<? extends Annotation> scopeAnnotation,
|
||||||
Annotation annotation) {
|
Annotation annotation) {
|
||||||
super(key,
|
super(key,
|
||||||
method,
|
method,
|
||||||
instance,
|
instance,
|
||||||
dependencies,
|
dependencies,
|
||||||
parameterProviders,
|
|
||||||
scopeAnnotation,
|
scopeAnnotation,
|
||||||
annotation);
|
annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
Object doProvision(Object[] parameters) throws IllegalAccessException,
|
T doProvision(Object[] parameters) throws IllegalAccessException,
|
||||||
InvocationTargetException {
|
InvocationTargetException {
|
||||||
return method.invoke(instance, parameters);
|
return (T) method.invoke(instance, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
|
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.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.spi.Dependency;
|
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
|
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
|
||||||
|
@ -21,11 +18,12 @@ import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
|
* Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
|
||||||
|
@ -34,23 +32,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
*/
|
*/
|
||||||
public final class ProviderMethodsModule implements Module {
|
public final class ProviderMethodsModule implements Module {
|
||||||
|
|
||||||
private static ModuleAnnotatedMethodScanner PROVIDES_BUILDER =
|
|
||||||
new ModuleAnnotatedMethodScanner() {
|
|
||||||
@Override
|
|
||||||
public <T> Key<T> prepareMethod(
|
|
||||||
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<? extends Class<? extends Annotation>> annotationClasses() {
|
|
||||||
return ImmutableSet.of(Provides.class);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Object delegate;
|
private final Object delegate;
|
||||||
|
|
||||||
private final TypeLiteral<?> typeLiteral;
|
private final TypeLiteral<?> typeLiteral;
|
||||||
|
|
||||||
private final boolean skipFastClassGeneration;
|
private final boolean skipFastClassGeneration;
|
||||||
|
|
||||||
private final ModuleAnnotatedMethodScanner scanner;
|
private final ModuleAnnotatedMethodScanner scanner;
|
||||||
|
|
||||||
private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration,
|
private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration,
|
||||||
|
@ -65,7 +52,7 @@ public final class ProviderMethodsModule implements Module {
|
||||||
* Returns a module which creates bindings for provider methods from the given module.
|
* Returns a module which creates bindings for provider methods from the given module.
|
||||||
*/
|
*/
|
||||||
public static Module forModule(Module module) {
|
public static Module forModule(Module module) {
|
||||||
return forObject(module, false, PROVIDES_BUILDER);
|
return forObject(module, false, ProvidesMethodScanner.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,7 +70,7 @@ public final class ProviderMethodsModule implements Module {
|
||||||
* are only interested in Module metadata.
|
* are only interested in Module metadata.
|
||||||
*/
|
*/
|
||||||
public static Module forObject(Object object) {
|
public static Module forObject(Object object) {
|
||||||
return forObject(object, true, PROVIDES_BUILDER);
|
return forObject(object, true, ProvidesMethodScanner.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Module forObject(Object object, boolean skipFastClassGeneration,
|
private static Module forObject(Object object, boolean skipFastClassGeneration,
|
||||||
|
@ -92,63 +79,95 @@ public final class ProviderMethodsModule implements Module {
|
||||||
if (object instanceof ProviderMethodsModule) {
|
if (object instanceof ProviderMethodsModule) {
|
||||||
return Modules.EMPTY_MODULE;
|
return Modules.EMPTY_MODULE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ProviderMethodsModule(object, skipFastClassGeneration, scanner);
|
return new ProviderMethodsModule(object, skipFastClassGeneration, scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Class<?> getDelegateModuleClass() {
|
||||||
* Returns true if a overrides b, assumes that the signatures match
|
return isStaticModule() ? (Class<?>) delegate : delegate.getClass();
|
||||||
*/
|
|
||||||
private static boolean overrides(Method a, Method b) {
|
|
||||||
// See JLS section 8.4.8.1
|
|
||||||
int modifiers = b.getModifiers();
|
|
||||||
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (Modifier.isPrivate(modifiers)) {
|
|
||||||
return false;
|
@Override
|
||||||
|
public void configure(Binder binder) {
|
||||||
|
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
|
||||||
|
providerMethod.configure(binder);
|
||||||
}
|
}
|
||||||
// b must be package-private
|
}
|
||||||
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
|
|
||||||
|
private boolean isStaticModule() {
|
||||||
|
return delegate instanceof Class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getDelegateModule() {
|
public Object getDelegateModule() {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public List<ProviderMethod<?>> getProviderMethods(Binder binder) {
|
||||||
public synchronized void configure(Binder binder) {
|
List<ProviderMethod<?>> result = null;
|
||||||
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
|
List<MethodAndAnnotation> methodsAndAnnotations = null;
|
||||||
providerMethod.configure(binder);
|
// The highest class in the type hierarchy that contained a provider method definition.
|
||||||
|
Class<?> superMostClass = getDelegateModuleClass();
|
||||||
|
for (Class<?> c = superMostClass; c != Object.class && c != null; c = c.getSuperclass()) {
|
||||||
|
for (Method method : DeclaredMembers.getDeclaredMethods(c)) {
|
||||||
|
Annotation annotation = getAnnotation(binder, method);
|
||||||
|
if (annotation != null) {
|
||||||
|
if (isStaticModule()
|
||||||
|
&& !Modifier.isStatic(method.getModifiers())
|
||||||
|
&& !Modifier.isAbstract(method.getModifiers())) {
|
||||||
|
binder
|
||||||
|
.skipSources(ProviderMethodsModule.class)
|
||||||
|
.addError(
|
||||||
|
"%s is an instance method, but a class literal was passed. Make this method"
|
||||||
|
+ " static or pass an instance of the module instead.",
|
||||||
|
method);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = new ArrayList<>();
|
||||||
|
methodsAndAnnotations = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ProviderMethod<?>> getProviderMethods(Binder binder) {
|
ProviderMethod<Object> providerMethod = createProviderMethod(binder, method, annotation);
|
||||||
List<ProviderMethod<?>> result = Lists.newArrayList();
|
if (providerMethod != null) {
|
||||||
Multimap<Signature, Method> methodsBySignature = HashMultimap.create();
|
result.add(providerMethod);
|
||||||
for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) {
|
}
|
||||||
|
methodsAndAnnotations.add(new MethodAndAnnotation(method, annotation));
|
||||||
|
superMostClass = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
// We didn't find anything
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
// We have found some provider methods, now we need to check if any were overridden.
|
||||||
|
// We do this as a separate pass to avoid calculating all the signatures when there are no
|
||||||
|
// provides methods, or when all provides methods are defined in a single class.
|
||||||
|
Multimap<Signature, Method> methodsBySignature = null;
|
||||||
|
// We can stop scanning when we see superMostClass, since no superclass method can override
|
||||||
|
// a method in a subclass. Corrollary, if superMostClass == getDelegateModuleClass(), there can
|
||||||
|
// be no overrides of a provides method.
|
||||||
|
for (Class<?> c = getDelegateModuleClass(); c != superMostClass; c = c.getSuperclass()) {
|
||||||
for (Method method : c.getDeclaredMethods()) {
|
for (Method method : c.getDeclaredMethods()) {
|
||||||
// private/static methods cannot override or be overridden by other methods, so there is no
|
|
||||||
// point in indexing them.
|
|
||||||
// Skip synthetic methods and bridge methods since java will automatically generate
|
|
||||||
// synthetic overrides in some cases where we don't want to generate an error (e.g.
|
|
||||||
// increasing visibility of a subclass).
|
|
||||||
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0)
|
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0)
|
||||||
&& !method.isBridge() && !method.isSynthetic()) {
|
&& !method.isBridge()
|
||||||
methodsBySignature.put(new Signature(method), method);
|
&& !method.isSynthetic()) {
|
||||||
|
if (methodsBySignature == null) {
|
||||||
|
methodsBySignature = HashMultimap.create();
|
||||||
}
|
}
|
||||||
Optional<Annotation> annotation = isProvider(binder, method);
|
methodsBySignature.put(new Signature(typeLiteral, method), method);
|
||||||
if (annotation.isPresent()) {
|
|
||||||
result.add(createProviderMethod(binder, method, annotation.get()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// we have found all the providers and now need to identify if any were overridden
|
if (methodsBySignature != null) {
|
||||||
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is only
|
// we have found all the signatures and now need to identify if any were overridden
|
||||||
// assuming that every method is an override, in general it should be very quick.
|
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is
|
||||||
for (ProviderMethod<?> provider : result) {
|
// only assuming that every method is an override, in general it should be very quick.
|
||||||
Method method = provider.getMethod();
|
for (MethodAndAnnotation methodAndAnnotation : methodsAndAnnotations) {
|
||||||
for (Method matchingSignature : methodsBySignature.get(new Signature(method))) {
|
Method method = methodAndAnnotation.method;
|
||||||
|
Annotation annotation = methodAndAnnotation.annotation;
|
||||||
|
|
||||||
|
for (Method matchingSignature :
|
||||||
|
methodsBySignature.get(new Signature(typeLiteral, method))) {
|
||||||
// matching signature is in the same class or a super class, therefore method cannot be
|
// matching signature is in the same class or a super class, therefore method cannot be
|
||||||
// overridding it.
|
// overridding it.
|
||||||
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
|
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
|
||||||
|
@ -156,79 +175,47 @@ public final class ProviderMethodsModule implements Module {
|
||||||
}
|
}
|
||||||
// now we know matching signature is in a subtype of method.getDeclaringClass()
|
// now we know matching signature is in a subtype of method.getDeclaringClass()
|
||||||
if (overrides(matchingSignature, method)) {
|
if (overrides(matchingSignature, method)) {
|
||||||
String annotationString = provider.getAnnotation().annotationType() == Provides.class
|
String annotationString =
|
||||||
? "@Provides" : "@" + provider.getAnnotation().annotationType().getCanonicalName();
|
annotation.annotationType() == Provides.class
|
||||||
|
? "@Provides"
|
||||||
|
: "@" + annotation.annotationType().getCanonicalName();
|
||||||
binder.addError(
|
binder.addError(
|
||||||
"Overriding " + annotationString + " methods is not allowed."
|
"Overriding "
|
||||||
+ "\n\t" + annotationString + " method: %s\n\toverridden by: %s",
|
+ annotationString
|
||||||
|
+ " methods is not allowed."
|
||||||
|
+ "\n\t"
|
||||||
|
+ annotationString
|
||||||
|
+ " method: %s\n\toverridden by: %s",
|
||||||
method,
|
method,
|
||||||
matchingSignature);
|
matchingSignature);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the annotation that is claimed by the scanner, or null if there is none. */
|
||||||
* Returns true if the method is a provider.
|
private Annotation getAnnotation(Binder binder, Method method) {
|
||||||
*
|
|
||||||
* Synthetic bridge methods are excluded. Starting with JDK 8, javac copies annotations onto
|
|
||||||
* bridge methods (which always have erased signatures).
|
|
||||||
*/
|
|
||||||
private Optional<Annotation> isProvider(Binder binder, Method method) {
|
|
||||||
if (method.isBridge() || method.isSynthetic()) {
|
if (method.isBridge() || method.isSynthetic()) {
|
||||||
return Optional.absent();
|
return null;
|
||||||
}
|
}
|
||||||
Annotation annotation = null;
|
Annotation annotation = null;
|
||||||
for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) {
|
for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) {
|
||||||
Annotation foundAnnotation = method.getAnnotation(annotationClass);
|
Annotation foundAnnotation = method.getAnnotation(annotationClass);
|
||||||
if (foundAnnotation != null) {
|
if (foundAnnotation != null) {
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
binder.addError("More than one annotation claimed by %s on method %s."
|
binder.addError(
|
||||||
|
"More than one annotation claimed by %s on method %s."
|
||||||
+ " Methods can only have one annotation claimed per scanner.",
|
+ " Methods can only have one annotation claimed per scanner.",
|
||||||
scanner, method);
|
scanner, method);
|
||||||
return Optional.absent();
|
return null;
|
||||||
}
|
}
|
||||||
annotation = foundAnnotation;
|
annotation = foundAnnotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Optional.fromNullable(annotation);
|
return annotation;
|
||||||
}
|
|
||||||
|
|
||||||
private <T> ProviderMethod<T> createProviderMethod(Binder binder, Method method,
|
|
||||||
Annotation annotation) {
|
|
||||||
binder = binder.withSource(method);
|
|
||||||
Errors errors = new Errors(method);
|
|
||||||
|
|
||||||
// prepare the parameter providers
|
|
||||||
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
|
|
||||||
List<Dependency<?>> dependencies = point.getDependencies();
|
|
||||||
List<Provider<?>> parameterProviders = Lists.newArrayList();
|
|
||||||
for (Dependency<?> dependency : point.getDependencies()) {
|
|
||||||
parameterProviders.add(binder.getProvider(dependency));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // Define T as the method's return type.
|
|
||||||
TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
|
|
||||||
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
|
|
||||||
try {
|
|
||||||
key = scanner.prepareMethod(binder, annotation, key, point);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
binder.addError(t);
|
|
||||||
}
|
|
||||||
Class<? extends Annotation> scopeAnnotation
|
|
||||||
= Annotations.findScopeAnnotation(errors, method.getAnnotations());
|
|
||||||
for (Message message : errors.getMessages()) {
|
|
||||||
binder.addError(message);
|
|
||||||
}
|
|
||||||
return ProviderMethod.create(key, method, delegate, ImmutableSet.copyOf(dependencies),
|
|
||||||
parameterProviders, scopeAnnotation, skipFastClassGeneration, annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
|
|
||||||
Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
|
|
||||||
return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -243,12 +230,23 @@ public final class ProviderMethodsModule implements Module {
|
||||||
return delegate.hashCode();
|
return delegate.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class Signature {
|
private static class MethodAndAnnotation {
|
||||||
|
final Method method;
|
||||||
|
final Annotation annotation;
|
||||||
|
|
||||||
|
MethodAndAnnotation(Method method, Annotation annotation) {
|
||||||
|
this.method = method;
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class Signature {
|
||||||
final Class<?>[] parameters;
|
final Class<?>[] parameters;
|
||||||
final String name;
|
final String name;
|
||||||
final int hashCode;
|
final int hashCode;
|
||||||
|
|
||||||
Signature(Method method) {
|
Signature(TypeLiteral<?> typeLiteral, Method method) {
|
||||||
this.name = method.getName();
|
this.name = method.getName();
|
||||||
// We need to 'resolve' the parameters against the actual class type in case this method uses
|
// We need to 'resolve' the parameters against the actual class type in case this method uses
|
||||||
// type parameters. This is so we can detect overrides of generic superclass methods where
|
// type parameters. This is so we can detect overrides of generic superclass methods where
|
||||||
|
@ -278,4 +276,75 @@ public final class ProviderMethodsModule implements Module {
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns true if a overrides b, assumes that the signatures match */
|
||||||
|
private static boolean overrides(Method a, Method b) {
|
||||||
|
// See JLS section 8.4.8.1
|
||||||
|
int modifiers = b.getModifiers();
|
||||||
|
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Modifier.isPrivate(modifiers)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// b must be package-private
|
||||||
|
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> ProviderMethod<T> createProviderMethod(
|
||||||
|
Binder binder, Method method, Annotation annotation) {
|
||||||
|
binder = binder.withSource(method);
|
||||||
|
Errors errors = new Errors(method);
|
||||||
|
|
||||||
|
// prepare the parameter providers
|
||||||
|
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
|
||||||
|
@SuppressWarnings("unchecked") // Define T as the method's return type.
|
||||||
|
TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
|
||||||
|
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
|
||||||
|
try {
|
||||||
|
key = scanner.prepareMethod(binder, annotation, key, point);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
binder.addError(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Modifier.isAbstract(method.getModifiers())) {
|
||||||
|
checkState(
|
||||||
|
key == null,
|
||||||
|
"%s returned a non-null key (%s) for %s. prepareMethod() must return null for abstract"
|
||||||
|
+ " methods",
|
||||||
|
scanner,
|
||||||
|
key,
|
||||||
|
method);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
checkState(
|
||||||
|
key != null,
|
||||||
|
"%s returned a null key for %s. prepareMethod() can only return null for abstract"
|
||||||
|
+ " methods",
|
||||||
|
scanner,
|
||||||
|
method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<? extends Annotation> scopeAnnotation =
|
||||||
|
Annotations.findScopeAnnotation(errors, method.getAnnotations());
|
||||||
|
for (Message message : errors.getMessages()) {
|
||||||
|
binder.addError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProviderMethod.create(
|
||||||
|
key,
|
||||||
|
method,
|
||||||
|
isStaticModule() ? null : delegate,
|
||||||
|
ImmutableSet.copyOf(point.getDependencies()),
|
||||||
|
scopeAnnotation,
|
||||||
|
skipFastClassGeneration,
|
||||||
|
annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
|
||||||
|
Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
|
||||||
|
return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.ProvisionException;
|
|
||||||
import com.google.inject.spi.Dependency;
|
|
||||||
|
|
||||||
final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
|
final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
|
||||||
|
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private final InternalFactory<? extends T> internalFactory;
|
private final InternalFactory<? extends T> internalFactory;
|
||||||
|
|
||||||
public ProviderToInternalFactoryAdapter(InjectorImpl injector,
|
public ProviderToInternalFactoryAdapter(InjectorImpl injector,
|
||||||
|
@ -15,23 +14,21 @@ final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
|
||||||
this.internalFactory = internalFactory;
|
this.internalFactory = internalFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public T get() {
|
public T get() {
|
||||||
final Errors errors = new Errors();
|
try (InternalContext context = injector.enterContext()) {
|
||||||
try {
|
|
||||||
T t = injector.callInContext(new ContextualCallable<T>() {
|
|
||||||
public T call(InternalContext context) throws ErrorsException {
|
|
||||||
Dependency dependency = context.getDependency();
|
|
||||||
// Always pretend that we are a linked binding, to support
|
// Always pretend that we are a linked binding, to support
|
||||||
// scoping implicit bindings. If we are not actually a linked
|
// scoping implicit bindings. If we are not actually a linked
|
||||||
// binding, we'll fail properly elsewhere in the chain.
|
// binding, we'll fail properly elsewhere in the chain.
|
||||||
return internalFactory.get(errors, context, dependency, true);
|
return internalFactory.get(context, context.getDependency(), true);
|
||||||
|
} catch (InternalProvisionException e) {
|
||||||
|
throw e.toProvisionException();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
errors.throwIfNewErrors(0);
|
|
||||||
return t;
|
|
||||||
} catch (ErrorsException e) {
|
|
||||||
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Exposed for SingletonScope. */
|
||||||
|
InjectorImpl getInjector() {
|
||||||
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.multibindings.MapKey;
|
||||||
|
import com.google.inject.multibindings.ProvidesIntoMap;
|
||||||
|
import com.google.inject.multibindings.ProvidesIntoOptional;
|
||||||
|
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ModuleAnnotatedMethodScanner} that handles the {@link Provides}, {@link ProvidesIntoSet},
|
||||||
|
* {@link ProvidesIntoMap} and {@link ProvidesIntoOptional} annotations.
|
||||||
|
*
|
||||||
|
* <p>This is the default scanner used by ProviderMethodsModule and handles all the built in
|
||||||
|
* annotations.
|
||||||
|
*/
|
||||||
|
final class ProvidesMethodScanner extends ModuleAnnotatedMethodScanner {
|
||||||
|
static final ProvidesMethodScanner INSTANCE = new ProvidesMethodScanner();
|
||||||
|
private static final ImmutableSet<Class<? extends Annotation>> ANNOTATIONS =
|
||||||
|
ImmutableSet.of(Provides.class, ProvidesIntoSet.class, ProvidesIntoMap.class, ProvidesIntoOptional.class);
|
||||||
|
|
||||||
|
private ProvidesMethodScanner() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<? extends Class<? extends Annotation>> annotationClasses() {
|
||||||
|
return ANNOTATIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"}) // mapKey doesn't know its key type
|
||||||
|
@Override
|
||||||
|
public <T> Key<T> prepareMethod(
|
||||||
|
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
|
||||||
|
Method method = (Method) injectionPoint.getMember();
|
||||||
|
AnnotationOrError mapKey = findMapKeyAnnotation(binder, method);
|
||||||
|
if (annotation instanceof Provides) {
|
||||||
|
if (mapKey.annotation != null) {
|
||||||
|
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
|
||||||
|
}
|
||||||
|
// no key rewriting for plain old @Provides
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
if (annotation instanceof ProvidesIntoSet) {
|
||||||
|
if (mapKey.annotation != null) {
|
||||||
|
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
|
||||||
|
}
|
||||||
|
return RealMultibinder.newRealSetBinder(binder, key).getKeyForNewItem();
|
||||||
|
} else if (annotation instanceof ProvidesIntoMap) {
|
||||||
|
if (mapKey.error) {
|
||||||
|
// Already failed on the MapKey, don't bother doing more work.
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
if (mapKey.annotation == null) {
|
||||||
|
// If no MapKey, make an error and abort.
|
||||||
|
binder.addError("No MapKey found for map binding at %s.", method);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
TypeAndValue typeAndValue = typeAndValueOfMapKey(mapKey.annotation);
|
||||||
|
return RealMapBinder.newRealMapBinder(binder, typeAndValue.type, key)
|
||||||
|
.getKeyForNewValue(typeAndValue.value);
|
||||||
|
} else if (annotation instanceof ProvidesIntoOptional) {
|
||||||
|
if (mapKey.annotation != null) {
|
||||||
|
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
|
||||||
|
}
|
||||||
|
switch (((ProvidesIntoOptional) annotation).value()) {
|
||||||
|
case DEFAULT:
|
||||||
|
return RealOptionalBinder.newRealOptionalBinder(binder, key).getKeyForDefaultBinding();
|
||||||
|
case ACTUAL:
|
||||||
|
return RealOptionalBinder.newRealOptionalBinder(binder, key).getKeyForActualBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Invalid annotation: " + annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnnotationOrError {
|
||||||
|
final Annotation annotation;
|
||||||
|
final boolean error;
|
||||||
|
|
||||||
|
AnnotationOrError(Annotation annotation, boolean error) {
|
||||||
|
this.annotation = annotation;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AnnotationOrError forPossiblyNullAnnotation(Annotation annotation) {
|
||||||
|
return new AnnotationOrError(annotation, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AnnotationOrError forError() {
|
||||||
|
return new AnnotationOrError(null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnnotationOrError findMapKeyAnnotation(Binder binder, Method method) {
|
||||||
|
Annotation foundAnnotation = null;
|
||||||
|
for (Annotation annotation : method.getAnnotations()) {
|
||||||
|
MapKey mapKey = annotation.annotationType().getAnnotation(MapKey.class);
|
||||||
|
if (mapKey != null) {
|
||||||
|
if (foundAnnotation != null) {
|
||||||
|
binder.addError("Found more than one MapKey annotations on %s.", method);
|
||||||
|
return AnnotationOrError.forError();
|
||||||
|
}
|
||||||
|
if (mapKey.unwrapValue()) {
|
||||||
|
try {
|
||||||
|
// validate there's a declared method called "value"
|
||||||
|
Method valueMethod = annotation.annotationType().getDeclaredMethod("value");
|
||||||
|
if (valueMethod.getReturnType().isArray()) {
|
||||||
|
binder.addError(
|
||||||
|
"Array types are not allowed in a MapKey with unwrapValue=true: %s",
|
||||||
|
annotation.annotationType());
|
||||||
|
return AnnotationOrError.forError();
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException invalid) {
|
||||||
|
binder.addError(
|
||||||
|
"No 'value' method in MapKey with unwrapValue=true: %s",
|
||||||
|
annotation.annotationType());
|
||||||
|
return AnnotationOrError.forError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foundAnnotation = annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AnnotationOrError.forPossiblyNullAnnotation(foundAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private static TypeAndValue<?> typeAndValueOfMapKey(Annotation mapKeyAnnotation) {
|
||||||
|
if (!mapKeyAnnotation.annotationType().getAnnotation(MapKey.class).unwrapValue()) {
|
||||||
|
return new TypeAndValue(TypeLiteral.get(mapKeyAnnotation.annotationType()), mapKeyAnnotation);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Method valueMethod = mapKeyAnnotation.annotationType().getDeclaredMethod("value");
|
||||||
|
valueMethod.setAccessible(true);
|
||||||
|
TypeLiteral<?> returnType =
|
||||||
|
TypeLiteral.get(mapKeyAnnotation.annotationType()).getReturnType(valueMethod);
|
||||||
|
return new TypeAndValue(returnType, valueMethod.invoke(mapKeyAnnotation));
|
||||||
|
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TypeAndValue<T> {
|
||||||
|
final TypeLiteral<T> type;
|
||||||
|
final T value;
|
||||||
|
|
||||||
|
TypeAndValue(TypeLiteral<T> type, T value) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,9 +29,9 @@ final class ProvisionListenerCallbackStore {
|
||||||
|
|
||||||
private final ImmutableList<ProvisionListenerBinding> listenerBindings;
|
private final ImmutableList<ProvisionListenerBinding> listenerBindings;
|
||||||
|
|
||||||
private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache
|
private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache = CacheBuilder.newBuilder().build(
|
||||||
= CacheBuilder.newBuilder().build(
|
new CacheLoader<>() {
|
||||||
new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() {
|
@Override
|
||||||
public ProvisionListenerStackCallback<?> load(KeyBinding key) {
|
public ProvisionListenerStackCallback<?> load(KeyBinding key) {
|
||||||
return create(key.binding);
|
return create(key.binding);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ package com.google.inject.internal;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.ProvisionException;
|
|
||||||
import com.google.inject.spi.DependencyAndSource;
|
|
||||||
import com.google.inject.spi.ProvisionListener;
|
import com.google.inject.spi.ProvisionListener;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -16,7 +14,8 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
final class ProvisionListenerStackCallback<T> {
|
final class ProvisionListenerStackCallback<T> {
|
||||||
|
|
||||||
private static final ProvisionListener EMPTY_LISTENER[] = new ProvisionListener[0];
|
private static final ProvisionListener[] EMPTY_LISTENER = new ProvisionListener[0];
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
|
private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
|
||||||
new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of());
|
new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of());
|
||||||
|
@ -43,9 +42,9 @@ final class ProvisionListenerStackCallback<T> {
|
||||||
return listeners.length > 0;
|
return listeners.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T provision(Errors errors, InternalContext context, ProvisionCallback<T> callable)
|
public T provision(InternalContext context, ProvisionCallback<T> callable)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
Provision provision = new Provision(errors, context, callable);
|
Provision provision = new Provision(context, callable);
|
||||||
RuntimeException caught = null;
|
RuntimeException caught = null;
|
||||||
try {
|
try {
|
||||||
provision.provision();
|
provision.provision();
|
||||||
|
@ -56,12 +55,14 @@ final class ProvisionListenerStackCallback<T> {
|
||||||
if (provision.exceptionDuringProvision != null) {
|
if (provision.exceptionDuringProvision != null) {
|
||||||
throw provision.exceptionDuringProvision;
|
throw provision.exceptionDuringProvision;
|
||||||
} else if (caught != null) {
|
} else if (caught != null) {
|
||||||
Object listener = provision.erredListener != null ?
|
Object listener =
|
||||||
provision.erredListener.getClass() : "(unknown)";
|
provision.erredListener != null ? provision.erredListener.getClass() : "(unknown)";
|
||||||
throw errors
|
throw InternalProvisionException.errorInUserCode(
|
||||||
.errorInUserCode(caught, "Error notifying ProvisionListener %s of %s.%n"
|
caught,
|
||||||
+ " Reason: %s", listener, binding.getKey(), caught)
|
"Error notifying ProvisionListener %s of %s.%n Reason: %s",
|
||||||
.toException();
|
listener,
|
||||||
|
binding.getKey(),
|
||||||
|
caught);
|
||||||
} else {
|
} else {
|
||||||
return provision.result;
|
return provision.result;
|
||||||
}
|
}
|
||||||
|
@ -69,25 +70,21 @@ final class ProvisionListenerStackCallback<T> {
|
||||||
|
|
||||||
// TODO(sameb): Can this be more InternalFactory-like?
|
// TODO(sameb): Can this be more InternalFactory-like?
|
||||||
public interface ProvisionCallback<T> {
|
public interface ProvisionCallback<T> {
|
||||||
T call() throws ErrorsException;
|
T call() throws InternalProvisionException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Provision extends ProvisionListener.ProvisionInvocation<T> {
|
private class Provision extends ProvisionListener.ProvisionInvocation<T> {
|
||||||
|
|
||||||
final Errors errors;
|
|
||||||
final int numErrorsBefore;
|
|
||||||
final InternalContext context;
|
final InternalContext context;
|
||||||
final ProvisionCallback<T> callable;
|
final ProvisionCallback<T> callable;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
T result;
|
T result;
|
||||||
ErrorsException exceptionDuringProvision;
|
InternalProvisionException exceptionDuringProvision;
|
||||||
ProvisionListener erredListener;
|
ProvisionListener erredListener;
|
||||||
|
|
||||||
public Provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) {
|
public Provision(InternalContext context, ProvisionCallback<T> callable) {
|
||||||
this.callable = callable;
|
this.callable = callable;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.errors = errors;
|
|
||||||
this.numErrorsBefore = errors.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,12 +93,9 @@ final class ProvisionListenerStackCallback<T> {
|
||||||
if (index == listeners.length) {
|
if (index == listeners.length) {
|
||||||
try {
|
try {
|
||||||
result = callable.call();
|
result = callable.call();
|
||||||
// Make sure we don't return the provisioned object if there were any errors
|
} catch (InternalProvisionException ipe) {
|
||||||
// injecting its field/method dependencies.
|
exceptionDuringProvision = ipe;
|
||||||
errors.throwIfNewErrors(numErrorsBefore);
|
throw ipe.toProvisionException();
|
||||||
} catch (ErrorsException ee) {
|
|
||||||
exceptionDuringProvision = ee;
|
|
||||||
throw new ProvisionException(errors.merge(ee.getErrors()).getMessages());
|
|
||||||
}
|
}
|
||||||
} else if (index < listeners.length) {
|
} else if (index < listeners.length) {
|
||||||
int currentIdx = index;
|
int currentIdx = index;
|
||||||
|
@ -128,10 +122,5 @@ final class ProvisionListenerStackCallback<T> {
|
||||||
// if someone calls that they'll get strange errors.
|
// if someone calls that they'll get strange errors.
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<DependencyAndSource> getDependencyChain() {
|
|
||||||
return context.getDependencyChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.google.inject.multibindings;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.internal.Annotations;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
1352
src/main/java/com/google/inject/internal/RealMapBinder.java
Normal file
1352
src/main/java/com/google/inject/internal/RealMapBinder.java
Normal file
File diff suppressed because it is too large
Load diff
611
src/main/java/com/google/inject/internal/RealMultibinder.java
Normal file
611
src/main/java/com/google/inject/internal/RealMultibinder.java
Normal file
|
@ -0,0 +1,611 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.inject.internal.Element.Type.MULTIBINDER;
|
||||||
|
import static com.google.inject.internal.Errors.checkConfiguration;
|
||||||
|
import static com.google.inject.internal.Errors.checkNotNull;
|
||||||
|
import static com.google.inject.name.Names.named;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.binder.LinkedBindingBuilder;
|
||||||
|
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
|
||||||
|
import com.google.inject.multibindings.MultibinderBinding;
|
||||||
|
import com.google.inject.multibindings.MultibindingsTargetVisitor;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
|
import com.google.inject.spi.ProviderWithExtensionVisitor;
|
||||||
|
import com.google.inject.util.Types;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual multibinder plays several roles:
|
||||||
|
*
|
||||||
|
* <p>As a Multibinder, it acts as a factory for LinkedBindingBuilders for each of the set's
|
||||||
|
* elements. Each binding is given an annotation that identifies it as a part of this set.
|
||||||
|
*
|
||||||
|
* <p>As a Module, it installs the binding to the set itself. As a module, this implements equals()
|
||||||
|
* and hashcode() in order to trick Guice into executing its configure() method only once. That
|
||||||
|
* makes it so that multiple multibinders can be created for the same target collection, but only
|
||||||
|
* one is bound. Since the list of bindings is retrieved from the injector itself (and not the
|
||||||
|
* multibinder), each multibinder has access to all contributions from all multibinders.
|
||||||
|
*
|
||||||
|
* <p>As a Provider, this constructs the set instances.
|
||||||
|
*
|
||||||
|
* <p>We use a subclass to hide 'implements Module, Provider' from the public API.
|
||||||
|
*/
|
||||||
|
public final class RealMultibinder<T> implements Module {
|
||||||
|
|
||||||
|
/** Implementation of newSetBinder. */
|
||||||
|
public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key) {
|
||||||
|
binder = binder.skipSources(RealMultibinder.class);
|
||||||
|
RealMultibinder<T> result = new RealMultibinder<>(binder, key);
|
||||||
|
binder.install(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
|
||||||
|
static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
|
||||||
|
Type type = Types.setOf(elementType.getType());
|
||||||
|
return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
|
||||||
|
TypeLiteral<T> elementType) {
|
||||||
|
Type providerType = Types.providerOf(elementType.getType());
|
||||||
|
Type type = Types.collectionOf(providerType);
|
||||||
|
return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersOf(
|
||||||
|
TypeLiteral<T> elementType) {
|
||||||
|
Type providerType =
|
||||||
|
Types.newParameterizedType(javax.inject.Provider.class, elementType.getType());
|
||||||
|
Type type = Types.collectionOf(providerType);
|
||||||
|
return (TypeLiteral<Collection<javax.inject.Provider<T>>>) TypeLiteral.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BindingSelection<T> bindingSelection;
|
||||||
|
private final Binder binder;
|
||||||
|
|
||||||
|
RealMultibinder(Binder binder, Key<T> key) {
|
||||||
|
this.binder = checkNotNull(binder, "binder");
|
||||||
|
this.bindingSelection = new BindingSelection<>(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(Binder binder) {
|
||||||
|
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
|
||||||
|
binder
|
||||||
|
.bind(bindingSelection.getSetKey())
|
||||||
|
.toProvider(new RealMultibinderProvider<T>(bindingSelection));
|
||||||
|
Provider<Collection<Provider<T>>> collectionOfProvidersProvider =
|
||||||
|
new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection);
|
||||||
|
binder
|
||||||
|
.bind(bindingSelection.getCollectionOfProvidersKey())
|
||||||
|
.toProvider(collectionOfProvidersProvider);
|
||||||
|
|
||||||
|
// The collection this exposes is internally an ImmutableList, so it's OK to massage
|
||||||
|
// the guice Provider to javax Provider in the value (since the guice Provider implements
|
||||||
|
// javax Provider).
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Provider<Collection<javax.inject.Provider<T>>> javaxProvider =
|
||||||
|
(Provider) collectionOfProvidersProvider;
|
||||||
|
binder.bind(bindingSelection.getCollectionOfJavaxProvidersKey()).toProvider(javaxProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void permitDuplicates() {
|
||||||
|
binder.install(new PermitDuplicatesModule(bindingSelection.getPermitDuplicatesKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adds a new entry to the set and returns the key for it. */
|
||||||
|
Key<T> getKeyForNewItem() {
|
||||||
|
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
|
||||||
|
return Key.get(
|
||||||
|
bindingSelection.getElementTypeLiteral(),
|
||||||
|
new RealElement(bindingSelection.getSetName(), MULTIBINDER, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedBindingBuilder<T> addBinding() {
|
||||||
|
return binder.bind(getKeyForNewItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
// These methods are used by RealMapBinder
|
||||||
|
|
||||||
|
Key<Set<T>> getSetKey() {
|
||||||
|
return bindingSelection.getSetKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeLiteral<T> getElementTypeLiteral() {
|
||||||
|
return bindingSelection.getElementTypeLiteral();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSetName() {
|
||||||
|
return bindingSelection.getSetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean permitsDuplicates(Injector injector) {
|
||||||
|
return bindingSelection.permitsDuplicates(injector);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean containsElement(com.google.inject.spi.Element element) {
|
||||||
|
return bindingSelection.containsElement(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RealMultibinderProvider<T>
|
||||||
|
extends InternalProviderInstanceBindingImpl.Factory<Set<T>>
|
||||||
|
implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
|
||||||
|
private final BindingSelection<T> bindingSelection;
|
||||||
|
private List<Binding<T>> bindings;
|
||||||
|
private SingleParameterInjector<T>[] injectors;
|
||||||
|
private boolean permitDuplicates;
|
||||||
|
|
||||||
|
RealMultibinderProvider(BindingSelection<T> bindingSelection) {
|
||||||
|
// While Multibinders only depend on bindings created in modules so we could theoretically
|
||||||
|
// initialize eagerly, they also depend on
|
||||||
|
// 1. findBindingsByType returning results
|
||||||
|
// 2. being able to call BindingImpl.acceptTargetVisitor
|
||||||
|
// neither of those is available during eager initialization, so we use DELAYED
|
||||||
|
super(InitializationTiming.DELAYED);
|
||||||
|
this.bindingSelection = bindingSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.getDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
|
bindingSelection.initialize(injector, errors);
|
||||||
|
this.bindings = bindingSelection.getBindings();
|
||||||
|
this.injectors = bindingSelection.getParameterInjectors();
|
||||||
|
this.permitDuplicates = bindingSelection.permitsDuplicates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<T> doProvision(InternalContext context, Dependency<?> dependency)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
SingleParameterInjector<T>[] localInjectors = injectors;
|
||||||
|
if (localInjectors == null) {
|
||||||
|
// if localInjectors == null, then we have no bindings so return the empty set.
|
||||||
|
return ImmutableSet.of();
|
||||||
|
}
|
||||||
|
// Ideally we would just add to an ImmutableSet.Builder, but if we did that and there were
|
||||||
|
// duplicates we wouldn't be able to tell which one was the duplicate. So to manage this we
|
||||||
|
// first put everything into an array and then construct the set. This way if something gets
|
||||||
|
// dropped we can figure out what it is.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T[] values = (T[]) new Object[localInjectors.length];
|
||||||
|
for (int i = 0; i < localInjectors.length; i++) {
|
||||||
|
SingleParameterInjector<T> parameterInjector = localInjectors[i];
|
||||||
|
T newValue = parameterInjector.inject(context);
|
||||||
|
if (newValue == null) {
|
||||||
|
throw newNullEntryException(i);
|
||||||
|
}
|
||||||
|
values[i] = newValue;
|
||||||
|
}
|
||||||
|
ImmutableSet<T> set = ImmutableSet.copyOf(values);
|
||||||
|
// There are fewer items in the set than the array. Figure out which one got dropped.
|
||||||
|
if (!permitDuplicates && set.size() < values.length) {
|
||||||
|
throw newDuplicateValuesException(set, values);
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InternalProvisionException newNullEntryException(int i) {
|
||||||
|
return InternalProvisionException.create(
|
||||||
|
"Set injection failed due to null element bound at: %s", bindings.get(i).getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <B, V> V acceptExtensionVisitor(
|
||||||
|
BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) {
|
||||||
|
if (visitor instanceof MultibindingsTargetVisitor) {
|
||||||
|
return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
|
||||||
|
} else {
|
||||||
|
return visitor.visit(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InternalProvisionException newDuplicateValuesException(
|
||||||
|
ImmutableSet<T> set, T[] values) {
|
||||||
|
// TODO(lukes): consider reporting all duplicate values, the easiest way would be to rebuild
|
||||||
|
// a new set and detect dupes as we go
|
||||||
|
// Find the duplicate binding
|
||||||
|
// To do this we take advantage of the fact that set, values and bindings all have the same
|
||||||
|
// ordering for a non-empty prefix of the set.
|
||||||
|
// First we scan for the first item dropped from the set.
|
||||||
|
int newBindingIndex = 0;
|
||||||
|
for (T item : set) {
|
||||||
|
if (item != values[newBindingIndex]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newBindingIndex++;
|
||||||
|
}
|
||||||
|
// once we exit the loop newBindingIndex will point at the first item in values that was
|
||||||
|
// dropped.
|
||||||
|
|
||||||
|
Binding<T> newBinding = bindings.get(newBindingIndex);
|
||||||
|
T newValue = values[newBindingIndex];
|
||||||
|
// Now we scan again to find the index of the value, we are guaranteed to find it.
|
||||||
|
int oldBindingIndex = set.asList().indexOf(newValue);
|
||||||
|
T oldValue = values[oldBindingIndex];
|
||||||
|
Binding<T> duplicateBinding = bindings.get(oldBindingIndex);
|
||||||
|
String oldString = oldValue.toString();
|
||||||
|
String newString = newValue.toString();
|
||||||
|
if (Objects.equal(oldString, newString)) {
|
||||||
|
// When the value strings match, just show the source of the bindings
|
||||||
|
return InternalProvisionException.create(
|
||||||
|
"Set injection failed due to duplicated element \"%s\""
|
||||||
|
+ "\n Bound at %s\n Bound at %s",
|
||||||
|
newValue, duplicateBinding.getSource(), newBinding.getSource());
|
||||||
|
} else {
|
||||||
|
// When the value strings don't match, include them both as they may be useful for debugging
|
||||||
|
return InternalProvisionException.create(
|
||||||
|
"Set injection failed due to multiple elements comparing equal:"
|
||||||
|
+ "\n \"%s\"\n bound at %s"
|
||||||
|
+ "\n \"%s\"\n bound at %s",
|
||||||
|
oldValue, duplicateBinding.getSource(), newValue, newBinding.getSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof RealMultibinderProvider
|
||||||
|
&& bindingSelection.equals(((RealMultibinderProvider<?>) obj).bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return bindingSelection.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key<Set<T>> getSetKey() {
|
||||||
|
return bindingSelection.getSetKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Key<?>> getAlternateSetKeys() {
|
||||||
|
return ImmutableSet.of(
|
||||||
|
(Key<?>) bindingSelection.getCollectionOfProvidersKey(),
|
||||||
|
(Key<?>) bindingSelection.getCollectionOfJavaxProvidersKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeLiteral<?> getElementTypeLiteral() {
|
||||||
|
return bindingSelection.getElementTypeLiteral();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Binding<?>> getElements() {
|
||||||
|
return bindingSelection.getElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean permitsDuplicates() {
|
||||||
|
return bindingSelection.permitsDuplicates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsElement(com.google.inject.spi.Element element) {
|
||||||
|
return bindingSelection.containsElement(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class BindingSelection<T> {
|
||||||
|
// prior to initialization we declare just a dependency on the injector, but as soon as we are
|
||||||
|
// initialized we swap to dependencies on the elements.
|
||||||
|
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
|
||||||
|
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||||
|
private final TypeLiteral<T> elementType;
|
||||||
|
private final Key<Set<T>> setKey;
|
||||||
|
|
||||||
|
// these are all lazily allocated
|
||||||
|
private String setName;
|
||||||
|
private Key<Collection<Provider<T>>> collectionOfProvidersKey;
|
||||||
|
private Key<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersKey;
|
||||||
|
private Key<Boolean> permitDuplicatesKey;
|
||||||
|
|
||||||
|
private boolean isInitialized;
|
||||||
|
/* a binding for each element in the set. null until initialization, non-null afterwards */
|
||||||
|
private ImmutableList<Binding<T>> bindings;
|
||||||
|
|
||||||
|
// Starts out as Injector and gets set up properly after initialization
|
||||||
|
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
|
||||||
|
private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
|
||||||
|
|
||||||
|
/** whether duplicates are allowed. Possibly configured by a different instance */
|
||||||
|
private boolean permitDuplicates;
|
||||||
|
|
||||||
|
private SingleParameterInjector<T>[] parameterinjectors;
|
||||||
|
|
||||||
|
BindingSelection(Key<T> key) {
|
||||||
|
this.setKey = key.ofType(setOf(key.getTypeLiteral()));
|
||||||
|
this.elementType = key.getTypeLiteral();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
|
// This will be called multiple times, once by each Factory. We only want
|
||||||
|
// to do the work to initialize everything once, so guard this code with
|
||||||
|
// isInitialized.
|
||||||
|
if (isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Binding<T>> bindings = Lists.newArrayList();
|
||||||
|
Set<Indexer.IndexedBinding> index = Sets.newHashSet();
|
||||||
|
Indexer indexer = new Indexer(injector);
|
||||||
|
List<Dependency<?>> dependencies = Lists.newArrayList();
|
||||||
|
List<Dependency<?>> providerDependencies = Lists.newArrayList();
|
||||||
|
for (Binding<?> entry : injector.findBindingsByType(elementType)) {
|
||||||
|
if (keyMatches(entry.getKey())) {
|
||||||
|
@SuppressWarnings("unchecked") // protected by findBindingsByType()
|
||||||
|
Binding<T> binding = (Binding<T>) entry;
|
||||||
|
if (index.add(binding.acceptTargetVisitor(indexer))) {
|
||||||
|
// TODO(lukes): most of these are linked bindings since user bindings are linked to
|
||||||
|
// a user binding through the @Element annotation. Since this is an implementation
|
||||||
|
// detail we could 'dereference' the @Element if it is a LinkedBinding and avoid
|
||||||
|
// provisioning through the FactoryProxy at runtime.
|
||||||
|
// Ditto for OptionalBinder/MapBinder
|
||||||
|
bindings.add(binding);
|
||||||
|
Key<T> key = binding.getKey();
|
||||||
|
// TODO(lukes): we should mark this as a non-nullable dependency since we don't accept
|
||||||
|
// null.
|
||||||
|
// Add a dependency on Key<T>
|
||||||
|
dependencies.add(Dependency.get(key));
|
||||||
|
// and add a dependency on Key<Provider<T>>
|
||||||
|
providerDependencies.add(
|
||||||
|
Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bindings = ImmutableList.copyOf(bindings);
|
||||||
|
this.dependencies = ImmutableSet.copyOf(dependencies);
|
||||||
|
this.providerDependencies = ImmutableSet.copyOf(providerDependencies);
|
||||||
|
this.permitDuplicates = permitsDuplicates(injector);
|
||||||
|
// This is safe because all our dependencies are assignable to T and we never assign to
|
||||||
|
// elements of this array.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
SingleParameterInjector<T>[] typed =
|
||||||
|
(SingleParameterInjector<T>[]) injector.getParametersInjectors(dependencies, errors);
|
||||||
|
this.parameterinjectors = typed;
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean permitsDuplicates(Injector injector) {
|
||||||
|
return injector.getBindings().containsKey(getPermitDuplicatesKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableList<Binding<T>> getBindings() {
|
||||||
|
checkConfiguration(isInitialized, "not initialized");
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleParameterInjector<T>[] getParameterInjectors() {
|
||||||
|
checkConfiguration(isInitialized, "not initialized");
|
||||||
|
return parameterinjectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<Dependency<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<Dependency<?>> getProviderDependencies() {
|
||||||
|
return providerDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSetName() {
|
||||||
|
// lazily initialized since most selectors don't survive module installation.
|
||||||
|
if (setName == null) {
|
||||||
|
setName = Annotations.nameOf(setKey);
|
||||||
|
}
|
||||||
|
return setName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<Boolean> getPermitDuplicatesKey() {
|
||||||
|
Key<Boolean> local = permitDuplicatesKey;
|
||||||
|
if (local == null) {
|
||||||
|
local =
|
||||||
|
permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
|
||||||
|
}
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<Collection<Provider<T>>> getCollectionOfProvidersKey() {
|
||||||
|
Key<Collection<Provider<T>>> local = collectionOfProvidersKey;
|
||||||
|
if (local == null) {
|
||||||
|
local = collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
|
||||||
|
}
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<Collection<javax.inject.Provider<T>>> getCollectionOfJavaxProvidersKey() {
|
||||||
|
Key<Collection<javax.inject.Provider<T>>> local = collectionOfJavaxProvidersKey;
|
||||||
|
if (local == null) {
|
||||||
|
local =
|
||||||
|
collectionOfJavaxProvidersKey =
|
||||||
|
setKey.ofType(collectionOfJavaxProvidersOf(elementType));
|
||||||
|
}
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isInitialized() {
|
||||||
|
return isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultibinderBinding API methods
|
||||||
|
|
||||||
|
TypeLiteral<T> getElementTypeLiteral() {
|
||||||
|
return elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<Set<T>> getSetKey() {
|
||||||
|
return setKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Binding<?>> getElements() {
|
||||||
|
if (isInitialized()) {
|
||||||
|
return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("getElements() not supported for module bindings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean permitsDuplicates() {
|
||||||
|
if (isInitialized()) {
|
||||||
|
return permitDuplicates;
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"permitsDuplicates() not supported for module bindings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean containsElement(com.google.inject.spi.Element element) {
|
||||||
|
if (element instanceof Binding) {
|
||||||
|
Binding<?> binding = (Binding<?>) element;
|
||||||
|
return keyMatches(binding.getKey())
|
||||||
|
|| binding.getKey().equals(getPermitDuplicatesKey())
|
||||||
|
|| binding.getKey().equals(setKey)
|
||||||
|
|| binding.getKey().equals(collectionOfProvidersKey)
|
||||||
|
|| binding.getKey().equals(collectionOfJavaxProvidersKey);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean keyMatches(Key<?> key) {
|
||||||
|
return key.getTypeLiteral().equals(elementType)
|
||||||
|
&& key.getAnnotation() instanceof Element
|
||||||
|
&& ((Element) key.getAnnotation()).setName().equals(getSetName())
|
||||||
|
&& ((Element) key.getAnnotation()).type() == MULTIBINDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof BindingSelection) {
|
||||||
|
return setKey.equals(((BindingSelection<?>) obj).setKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return setKey.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (getSetName().isEmpty() ? "" : getSetName() + " ")
|
||||||
|
+ "Multibinder<"
|
||||||
|
+ elementType
|
||||||
|
+ ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof RealMultibinder
|
||||||
|
&& ((RealMultibinder<?>) o).bindingSelection.equals(bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return bindingSelection.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RealMultibinderCollectionOfProvidersProvider<T>
|
||||||
|
extends InternalProviderInstanceBindingImpl.Factory<Collection<Provider<T>>> {
|
||||||
|
|
||||||
|
private final BindingSelection<T> bindingSelection;
|
||||||
|
private ImmutableList<Provider<T>> collectionOfProviders;
|
||||||
|
|
||||||
|
RealMultibinderCollectionOfProvidersProvider(BindingSelection<T> bindingSelection) {
|
||||||
|
super(InitializationTiming.DELAYED); // See comment in RealMultibinderProvider
|
||||||
|
this.bindingSelection = bindingSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
|
bindingSelection.initialize(injector, errors);
|
||||||
|
ImmutableList.Builder<Provider<T>> providers = ImmutableList.builder();
|
||||||
|
for (Binding<T> binding : bindingSelection.getBindings()) {
|
||||||
|
providers.add(binding.getProvider());
|
||||||
|
}
|
||||||
|
this.collectionOfProviders = providers.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Provider<T>> doProvision(
|
||||||
|
InternalContext context, Dependency<?> dependency) {
|
||||||
|
return collectionOfProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.getProviderDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof RealMultibinderCollectionOfProvidersProvider
|
||||||
|
&& bindingSelection.equals(
|
||||||
|
((RealMultibinderCollectionOfProvidersProvider<?>) obj).bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return bindingSelection.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We install the permit duplicates configuration as its own binding, all by itself. This way, if
|
||||||
|
* only one of a multibinder's users remember to call permitDuplicates(), they're still permitted.
|
||||||
|
*
|
||||||
|
* <p>This is like setting a global variable in the injector so that each instance of the
|
||||||
|
* multibinder will have the same value for permitDuplicates, even if it is only set on one of
|
||||||
|
* them.
|
||||||
|
*/
|
||||||
|
private static class PermitDuplicatesModule extends AbstractModule {
|
||||||
|
private final Key<Boolean> key;
|
||||||
|
|
||||||
|
PermitDuplicatesModule(Key<Boolean> key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(key).toInstance(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof PermitDuplicatesModule && ((PermitDuplicatesModule) o).key.equals(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getClass().hashCode() ^ key.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
793
src/main/java/com/google/inject/internal/RealOptionalBinder.java
Normal file
793
src/main/java/com/google/inject/internal/RealOptionalBinder.java
Normal file
|
@ -0,0 +1,793 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.inject.internal.Errors.checkConfiguration;
|
||||||
|
import static com.google.inject.util.Types.newParameterizedType;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.binder.LinkedBindingBuilder;
|
||||||
|
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
|
||||||
|
import com.google.inject.multibindings.MultibindingsTargetVisitor;
|
||||||
|
import com.google.inject.multibindings.OptionalBinderBinding;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
|
import com.google.inject.spi.ProviderWithExtensionVisitor;
|
||||||
|
import com.google.inject.util.Types;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual OptionalBinder plays several roles. It implements Module to hide that fact from the
|
||||||
|
* public API, and installs the various bindings that are exposed to the user.
|
||||||
|
*/
|
||||||
|
public final class RealOptionalBinder<T> implements Module {
|
||||||
|
public static <T> RealOptionalBinder<T> newRealOptionalBinder(Binder binder, Key<T> type) {
|
||||||
|
binder = binder.skipSources(RealOptionalBinder.class);
|
||||||
|
RealOptionalBinder<T> optionalBinder = new RealOptionalBinder<>(binder, type);
|
||||||
|
binder.install(optionalBinder);
|
||||||
|
return optionalBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<Optional<T>> optionalOf(TypeLiteral<T> type) {
|
||||||
|
return (TypeLiteral<Optional<T>>)
|
||||||
|
TypeLiteral.get(Types.newParameterizedType(Optional.class, type.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<java.util.Optional<T>> javaOptionalOf(TypeLiteral<T> type) {
|
||||||
|
return (TypeLiteral<java.util.Optional<T>>)
|
||||||
|
TypeLiteral.get(Types.newParameterizedType(java.util.Optional.class, type.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<Optional<javax.inject.Provider<T>>> optionalOfJavaxProvider(
|
||||||
|
TypeLiteral<T> type) {
|
||||||
|
return (TypeLiteral<Optional<javax.inject.Provider<T>>>)
|
||||||
|
TypeLiteral.get(
|
||||||
|
Types.newParameterizedType(
|
||||||
|
Optional.class, newParameterizedType(javax.inject.Provider.class, type.getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<java.util.Optional<javax.inject.Provider<T>>> javaOptionalOfJavaxProvider(
|
||||||
|
TypeLiteral<T> type) {
|
||||||
|
return (TypeLiteral<java.util.Optional<javax.inject.Provider<T>>>)
|
||||||
|
TypeLiteral.get(
|
||||||
|
Types.newParameterizedType(
|
||||||
|
java.util.Optional.class,
|
||||||
|
newParameterizedType(javax.inject.Provider.class, type.getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<Optional<Provider<T>>> optionalOfProvider(TypeLiteral<T> type) {
|
||||||
|
return (TypeLiteral<Optional<Provider<T>>>)
|
||||||
|
TypeLiteral.get(
|
||||||
|
Types.newParameterizedType(
|
||||||
|
Optional.class, newParameterizedType(Provider.class, type.getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> TypeLiteral<java.util.Optional<Provider<T>>> javaOptionalOfProvider(
|
||||||
|
TypeLiteral<T> type) {
|
||||||
|
return (TypeLiteral<java.util.Optional<Provider<T>>>)
|
||||||
|
TypeLiteral.get(
|
||||||
|
Types.newParameterizedType(
|
||||||
|
java.util.Optional.class, newParameterizedType(Provider.class, type.getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> Key<Provider<T>> providerOf(Key<T> key) {
|
||||||
|
Type providerT = Types.providerOf(key.getTypeLiteral().getType());
|
||||||
|
return (Key<Provider<T>>) key.ofType(providerT);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Source {
|
||||||
|
DEFAULT,
|
||||||
|
ACTUAL
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Qualifier
|
||||||
|
@interface Default {
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Qualifier
|
||||||
|
@interface Actual {
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BindingSelection<T> bindingSelection;
|
||||||
|
private final Binder binder;
|
||||||
|
|
||||||
|
private RealOptionalBinder(Binder binder, Key<T> typeKey) {
|
||||||
|
this.bindingSelection = new BindingSelection<>(typeKey);
|
||||||
|
this.binder = binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a binding for T. Multiple calls to this are safe, and will be collapsed as duplicate
|
||||||
|
* bindings.
|
||||||
|
*/
|
||||||
|
private void addDirectTypeBinding(Binder binder) {
|
||||||
|
binder
|
||||||
|
.bind(bindingSelection.getDirectKey())
|
||||||
|
.toProvider(new RealDirectTypeProvider<T>(bindingSelection));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key to use for the default binding.
|
||||||
|
*
|
||||||
|
* <p>As a side effect this installs support for the 'direct type', so a binding for {@code T}
|
||||||
|
* will be made available.
|
||||||
|
*/
|
||||||
|
Key<T> getKeyForDefaultBinding() {
|
||||||
|
bindingSelection.checkNotInitialized();
|
||||||
|
addDirectTypeBinding(binder);
|
||||||
|
return bindingSelection.getKeyForDefaultBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedBindingBuilder<T> setDefault() {
|
||||||
|
return binder.bind(getKeyForDefaultBinding());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key to use for the actual binding, overrides the default if set.
|
||||||
|
*
|
||||||
|
* <p>As a side effect this installs support for the 'direct type', so a binding for {@code T}
|
||||||
|
* will be made available.
|
||||||
|
*/
|
||||||
|
Key<T> getKeyForActualBinding() {
|
||||||
|
bindingSelection.checkNotInitialized();
|
||||||
|
addDirectTypeBinding(binder);
|
||||||
|
return bindingSelection.getKeyForActualBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedBindingBuilder<T> setBinding() {
|
||||||
|
return binder.bind(getKeyForActualBinding());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(Binder binder) {
|
||||||
|
bindingSelection.checkNotInitialized();
|
||||||
|
Key<T> key = bindingSelection.getDirectKey();
|
||||||
|
TypeLiteral<T> typeLiteral = key.getTypeLiteral();
|
||||||
|
// Every OptionalBinder gets the following types bound
|
||||||
|
// * {cgcb,ju}.Optional<Provider<T>>
|
||||||
|
// * {cgcb,ju}.Optional<javax.inject.Provider<T>>
|
||||||
|
// * {cgcb,ju}.Optional<T>
|
||||||
|
// If setDefault() or setBinding() is called then also
|
||||||
|
// * T is bound
|
||||||
|
|
||||||
|
// cgcb.Optional<Provider<T>>
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<Optional<Provider<T>>> optionalProviderFactory =
|
||||||
|
new RealOptionalProviderProvider<>(bindingSelection);
|
||||||
|
binder.bind(key.ofType(optionalOfProvider(typeLiteral))).toProvider(optionalProviderFactory);
|
||||||
|
// ju.Optional<Provider<T>>
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<java.util.Optional<Provider<T>>>
|
||||||
|
javaOptionalProviderFactory = new JavaOptionalProviderProvider<>(bindingSelection);
|
||||||
|
binder
|
||||||
|
.bind(key.ofType(javaOptionalOfProvider(typeLiteral)))
|
||||||
|
.toProvider(javaOptionalProviderFactory);
|
||||||
|
|
||||||
|
// Provider is assignable to javax.inject.Provider and the provider that the factory contains
|
||||||
|
// cannot be modified so we can use some rawtypes hackery to share the same implementation.
|
||||||
|
|
||||||
|
// cgcb.Optional<ji.Provider<T>>
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<Optional<javax.inject.Provider<T>>>
|
||||||
|
optionalJavaxProviderFactory =
|
||||||
|
(InternalProviderInstanceBindingImpl.Factory) optionalProviderFactory;
|
||||||
|
binder
|
||||||
|
.bind(key.ofType(optionalOfJavaxProvider(typeLiteral)))
|
||||||
|
.toProvider(optionalJavaxProviderFactory);
|
||||||
|
// ju.Optional<ji.Provider<T>>
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<java.util.Optional<javax.inject.Provider<T>>>
|
||||||
|
javaOptionalJavaxProviderFactory =
|
||||||
|
(InternalProviderInstanceBindingImpl.Factory) javaOptionalProviderFactory;
|
||||||
|
binder
|
||||||
|
.bind(key.ofType(javaOptionalOfJavaxProvider(typeLiteral)))
|
||||||
|
.toProvider(javaOptionalJavaxProviderFactory);
|
||||||
|
|
||||||
|
// cgcb.Optional<T>
|
||||||
|
Key<Optional<T>> optionalKey = key.ofType(optionalOf(typeLiteral));
|
||||||
|
binder
|
||||||
|
.bind(optionalKey)
|
||||||
|
.toProvider(new RealOptionalKeyProvider<>(bindingSelection, optionalKey));
|
||||||
|
// ju.Optional<T>
|
||||||
|
Key<java.util.Optional<T>> javaOptionalKey = key.ofType(javaOptionalOf(typeLiteral));
|
||||||
|
binder
|
||||||
|
.bind(javaOptionalKey)
|
||||||
|
.toProvider(new JavaOptionalProvider<>(bindingSelection, javaOptionalKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides the binding for java.util.Optional<T>. */
|
||||||
|
private static final class JavaOptionalProvider<T>
|
||||||
|
extends RealOptionalBinderProviderWithDependencies<T, java.util.Optional<T>>
|
||||||
|
implements ProviderWithExtensionVisitor<java.util.Optional<T>>,
|
||||||
|
OptionalBinderBinding<java.util.Optional<T>> {
|
||||||
|
|
||||||
|
private final Key<java.util.Optional<T>> optionalKey;
|
||||||
|
|
||||||
|
private Dependency<?> targetDependency;
|
||||||
|
private InternalFactory<? extends T> target;
|
||||||
|
|
||||||
|
JavaOptionalProvider(
|
||||||
|
BindingSelection<T> bindingSelection, Key<java.util.Optional<T>> optionalKey) {
|
||||||
|
super(bindingSelection);
|
||||||
|
this.optionalKey = optionalKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void doInitialize() {
|
||||||
|
if (bindingSelection.getBinding() != null) {
|
||||||
|
target = bindingSelection.getBinding().getInternalFactory();
|
||||||
|
targetDependency = bindingSelection.getDependency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected java.util.Optional<T> doProvision(
|
||||||
|
InternalContext context, Dependency<?> currentDependency)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
InternalFactory<? extends T> local = target;
|
||||||
|
if (local == null) {
|
||||||
|
return java.util.Optional.empty();
|
||||||
|
}
|
||||||
|
Dependency<?> localDependency = targetDependency;
|
||||||
|
T result;
|
||||||
|
Dependency<?> previous = context.pushDependency(localDependency, getSource());
|
||||||
|
try {
|
||||||
|
// See comments in RealOptionalKeyProvider, about how localDependency may be more specific
|
||||||
|
// than what we actually need.
|
||||||
|
result = local.get(context, localDependency, false);
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(localDependency);
|
||||||
|
} finally {
|
||||||
|
context.popStateAndSetDependency(previous);
|
||||||
|
|
||||||
|
}
|
||||||
|
return java.util.Optional.ofNullable(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <B, R> R acceptExtensionVisitor(
|
||||||
|
BindingTargetVisitor<B, R> visitor, ProviderInstanceBinding<? extends B> binding) {
|
||||||
|
if (visitor instanceof MultibindingsTargetVisitor) {
|
||||||
|
return ((MultibindingsTargetVisitor<java.util.Optional<T>, R>) visitor).visit(this);
|
||||||
|
} else {
|
||||||
|
return visitor.visit(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsElement(Element element) {
|
||||||
|
return bindingSelection.containsElement(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Binding<?> getActualBinding() {
|
||||||
|
return bindingSelection.getActualBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Binding<?> getDefaultBinding() {
|
||||||
|
return bindingSelection.getDefaultBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key<java.util.Optional<T>> getKey() {
|
||||||
|
return optionalKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Key<?>> getAlternateKeys() {
|
||||||
|
Key<?> key = bindingSelection.getDirectKey();
|
||||||
|
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
|
||||||
|
return ImmutableSet.of(
|
||||||
|
(Key<?>) key.ofType(javaOptionalOfProvider(typeLiteral)),
|
||||||
|
(Key<?>) key.ofType(javaOptionalOfJavaxProvider(typeLiteral)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides the binding for java.util.Optional<Provider<T>>. */
|
||||||
|
private static final class JavaOptionalProviderProvider<T>
|
||||||
|
extends RealOptionalBinderProviderWithDependencies<T, java.util.Optional<Provider<T>>> {
|
||||||
|
private java.util.Optional<Provider<T>> value;
|
||||||
|
|
||||||
|
JavaOptionalProviderProvider(BindingSelection<T> bindingSelection) {
|
||||||
|
super(bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void doInitialize() {
|
||||||
|
if (bindingSelection.getBinding() == null) {
|
||||||
|
value = java.util.Optional.empty();
|
||||||
|
} else {
|
||||||
|
value = java.util.Optional.of(bindingSelection.getBinding().getProvider());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected java.util.Optional<Provider<T>> doProvision(
|
||||||
|
InternalContext context, Dependency<?> dependency) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.providerDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides the binding for T, conditionally installed by calling setBinding/setDefault. */
|
||||||
|
private static final class RealDirectTypeProvider<T>
|
||||||
|
extends RealOptionalBinderProviderWithDependencies<T, T> {
|
||||||
|
private Key<? extends T> targetKey;
|
||||||
|
|
||||||
|
private Object targetSource;
|
||||||
|
|
||||||
|
private InternalFactory<? extends T> targetFactory;
|
||||||
|
|
||||||
|
RealDirectTypeProvider(BindingSelection<T> bindingSelection) {
|
||||||
|
super(bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void doInitialize() {
|
||||||
|
BindingImpl<T> targetBinding = bindingSelection.getBinding();
|
||||||
|
// we only install this factory if they call setBinding()/setDefault() so we know that
|
||||||
|
// targetBinding will be non-null.
|
||||||
|
this.targetKey = targetBinding.getKey();
|
||||||
|
this.targetSource = targetBinding.getSource();
|
||||||
|
this.targetFactory = targetBinding.getInternalFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T doProvision(InternalContext context, Dependency<?> dependency)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
// This is what linked bindings do (see FactoryProxy), and we are pretty similar.
|
||||||
|
context.pushState(targetKey, targetSource);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return targetFactory.get(context, dependency, true);
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(targetKey);
|
||||||
|
} finally {
|
||||||
|
context.popState();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.dependencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides the binding for Optional<Provider<T>>. */
|
||||||
|
private static final class RealOptionalProviderProvider<T>
|
||||||
|
extends RealOptionalBinderProviderWithDependencies<T, Optional<Provider<T>>> {
|
||||||
|
private Optional<Provider<T>> value;
|
||||||
|
|
||||||
|
RealOptionalProviderProvider(BindingSelection<T> bindingSelection) {
|
||||||
|
super(bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void doInitialize() {
|
||||||
|
if (bindingSelection.getBinding() == null) {
|
||||||
|
value = Optional.absent();
|
||||||
|
} else {
|
||||||
|
value = Optional.of(bindingSelection.getBinding().getProvider());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<Provider<T>> doProvision(InternalContext context, Dependency<?> dependency) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.providerDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides the binding for Optional<T>. */
|
||||||
|
private static final class RealOptionalKeyProvider<T>
|
||||||
|
extends RealOptionalBinderProviderWithDependencies<T, Optional<T>>
|
||||||
|
implements ProviderWithExtensionVisitor<Optional<T>>, OptionalBinderBinding<Optional<T>> {
|
||||||
|
|
||||||
|
private final Key<Optional<T>> optionalKey;
|
||||||
|
|
||||||
|
// These are assigned to non-null values during initialization if and only if we have a binding
|
||||||
|
// to delegate to.
|
||||||
|
private Dependency<?> targetDependency;
|
||||||
|
private InternalFactory<? extends T> delegate;
|
||||||
|
|
||||||
|
RealOptionalKeyProvider(BindingSelection<T> bindingSelection, Key<Optional<T>> optionalKey) {
|
||||||
|
super(bindingSelection);
|
||||||
|
this.optionalKey = optionalKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void doInitialize() {
|
||||||
|
if (bindingSelection.getBinding() != null) {
|
||||||
|
delegate = bindingSelection.getBinding().getInternalFactory();
|
||||||
|
targetDependency = bindingSelection.getDependency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<T> doProvision(InternalContext context, Dependency<?> currentDependency)
|
||||||
|
throws InternalProvisionException {
|
||||||
|
InternalFactory<? extends T> local = delegate;
|
||||||
|
if (local == null) {
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
Dependency<?> localDependency = targetDependency;
|
||||||
|
T result;
|
||||||
|
Dependency<?> previous = context.pushDependency(localDependency, getSource());
|
||||||
|
try {
|
||||||
|
// currentDependency is Optional<? super T>, so we really just need to set the target
|
||||||
|
// dependency to ? super T, but we are currently setting it to T. We could hypothetically
|
||||||
|
// make it easier for our delegate to generate proxies by modifying the dependency, but that
|
||||||
|
// would also require us to rewrite the key on each call. So for now we don't do it.
|
||||||
|
result = local.get(context, localDependency, false);
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(localDependency);
|
||||||
|
} finally {
|
||||||
|
context.popStateAndSetDependency(previous);
|
||||||
|
|
||||||
|
}
|
||||||
|
return Optional.fromNullable(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return bindingSelection.dependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <B, R> R acceptExtensionVisitor(
|
||||||
|
BindingTargetVisitor<B, R> visitor, ProviderInstanceBinding<? extends B> binding) {
|
||||||
|
if (visitor instanceof MultibindingsTargetVisitor) {
|
||||||
|
return ((MultibindingsTargetVisitor<Optional<T>, R>) visitor).visit(this);
|
||||||
|
} else {
|
||||||
|
return visitor.visit(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key<Optional<T>> getKey() {
|
||||||
|
return optionalKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Key<?>> getAlternateKeys() {
|
||||||
|
Key<?> key = bindingSelection.getDirectKey();
|
||||||
|
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
|
||||||
|
return ImmutableSet.of(
|
||||||
|
(Key<?>) key.ofType(optionalOfProvider(typeLiteral)),
|
||||||
|
(Key<?>) key.ofType(optionalOfJavaxProvider(typeLiteral)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Binding<?> getActualBinding() {
|
||||||
|
return bindingSelection.getActualBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Binding<?> getDefaultBinding() {
|
||||||
|
return bindingSelection.getDefaultBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsElement(Element element) {
|
||||||
|
return bindingSelection.containsElement(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper object that implements the core logic for deciding what the implementation of the
|
||||||
|
* binding will be.
|
||||||
|
*
|
||||||
|
* <p>This also implements the main OptionalBinderBinding logic.
|
||||||
|
*/
|
||||||
|
private static final class BindingSelection<T> {
|
||||||
|
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
|
||||||
|
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||||
|
|
||||||
|
/*@Nullable */ BindingImpl<T> actualBinding;
|
||||||
|
/*@Nullable */ BindingImpl<T> defaultBinding;
|
||||||
|
/*@Nullable */ BindingImpl<T> binding;
|
||||||
|
private boolean initialized;
|
||||||
|
private final Key<T> key;
|
||||||
|
|
||||||
|
// Until the injector initializes us, we don't know what our dependencies are,
|
||||||
|
// so initialize to the whole Injector (like Multibinder, and MapBinder indirectly).
|
||||||
|
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
|
||||||
|
private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
|
||||||
|
|
||||||
|
/** lazily allocated, by {@link #getBindingName}. */
|
||||||
|
private String bindingName;
|
||||||
|
|
||||||
|
/** lazily allocated, by {@link #getKeyForDefaultBinding}. */
|
||||||
|
private Key<T> defaultBindingKey;
|
||||||
|
|
||||||
|
/** lazily allocated, by {@link #getKeyForActualBinding}. */
|
||||||
|
private Key<T> actualBindingKey;
|
||||||
|
|
||||||
|
BindingSelection(Key<T> key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkNotInitialized() {
|
||||||
|
checkConfiguration(!initialized, "already initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize(InjectorImpl injector) {
|
||||||
|
// Every one of our providers will call this method, so only execute the logic once.
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actualBinding = injector.getExistingBinding(getKeyForActualBinding());
|
||||||
|
defaultBinding = injector.getExistingBinding(getKeyForDefaultBinding());
|
||||||
|
// We should never create Jit bindings, but we can use them if some other binding created it.
|
||||||
|
BindingImpl<T> userBinding = injector.getExistingBinding(key);
|
||||||
|
if (actualBinding != null) {
|
||||||
|
// TODO(sameb): Consider exposing an option that will allow
|
||||||
|
// ACTUAL to fallback to DEFAULT if ACTUAL's provider returns null.
|
||||||
|
// Right now, an ACTUAL binding can convert from present -> absent
|
||||||
|
// if it's bound to a provider that returns null.
|
||||||
|
binding = actualBinding;
|
||||||
|
} else if (defaultBinding != null) {
|
||||||
|
binding = defaultBinding;
|
||||||
|
} else if (userBinding != null) {
|
||||||
|
// If neither the actual or default is set, then we fallback
|
||||||
|
// to the value bound to the type itself and consider that the
|
||||||
|
// "actual binding" for the SPI.
|
||||||
|
binding = userBinding;
|
||||||
|
actualBinding = userBinding;
|
||||||
|
}
|
||||||
|
if (binding != null) {
|
||||||
|
dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(binding.getKey()));
|
||||||
|
providerDependencies =
|
||||||
|
ImmutableSet.<Dependency<?>>of(Dependency.get(providerOf(binding.getKey())));
|
||||||
|
} else {
|
||||||
|
dependencies = ImmutableSet.of();
|
||||||
|
providerDependencies = ImmutableSet.of();
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<T> getKeyForDefaultBinding() {
|
||||||
|
if (defaultBindingKey == null) {
|
||||||
|
defaultBindingKey = Key.get(key.getTypeLiteral(), new DefaultImpl(getBindingName()));
|
||||||
|
}
|
||||||
|
return defaultBindingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<T> getKeyForActualBinding() {
|
||||||
|
if (actualBindingKey == null) {
|
||||||
|
actualBindingKey = Key.get(key.getTypeLiteral(), new ActualImpl(getBindingName()));
|
||||||
|
}
|
||||||
|
return actualBindingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key<T> getDirectKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBindingName() {
|
||||||
|
// Lazily allocated, most instantiations will never need this because they are deduped during
|
||||||
|
// module installation.
|
||||||
|
if (bindingName == null) {
|
||||||
|
bindingName = Annotations.nameOf(key);
|
||||||
|
}
|
||||||
|
return bindingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<T> getBinding() {
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide default implementations for most of the OptionalBinderBinding interface
|
||||||
|
BindingImpl<T> getDefaultBinding() {
|
||||||
|
return defaultBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<T> getActualBinding() {
|
||||||
|
return actualBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<Dependency<?>> providerDependencies() {
|
||||||
|
return providerDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<Dependency<?>> dependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Dependency for the target binding, throws NoSuchElementException if no target
|
||||||
|
* exists.
|
||||||
|
*
|
||||||
|
* <p>Calls to this method should typically be guarded by checking if {@link #getBinding()}
|
||||||
|
* returns {@code null}.
|
||||||
|
*/
|
||||||
|
Dependency<?> getDependency() {
|
||||||
|
return Iterables.getOnlyElement(dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of {@link OptionalBinderBinding#containsElement}. */
|
||||||
|
boolean containsElement(Element element) {
|
||||||
|
// All of our bindings are ProviderInstanceBindings whose providers extend
|
||||||
|
// RealOptionalBinderProviderWithDependencies and have 'this' as its binding selection.
|
||||||
|
if (element instanceof ProviderInstanceBinding) {
|
||||||
|
javax.inject.Provider<?> providerInstance =
|
||||||
|
((ProviderInstanceBinding<?>) element).getUserSuppliedProvider();
|
||||||
|
if (providerInstance instanceof RealOptionalBinderProviderWithDependencies) {
|
||||||
|
return ((RealOptionalBinderProviderWithDependencies<?, ?>) providerInstance)
|
||||||
|
.bindingSelection.equals(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (element instanceof Binding) {
|
||||||
|
Key<?> elementKey = ((Binding) element).getKey();
|
||||||
|
// if it isn't one of the things we bound directly it might be an actual or default key
|
||||||
|
return elementKey.equals(getKeyForActualBinding())
|
||||||
|
|| elementKey.equals(getKeyForDefaultBinding());
|
||||||
|
}
|
||||||
|
return false; // cannot match;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof BindingSelection && ((BindingSelection) o).key.equals(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return key.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof RealOptionalBinder
|
||||||
|
&& ((RealOptionalBinder<?>) o).bindingSelection.equals(bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return bindingSelection.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A base class for ProviderWithDependencies that need equality based on a specific object. */
|
||||||
|
private abstract static class RealOptionalBinderProviderWithDependencies<T, P>
|
||||||
|
extends InternalProviderInstanceBindingImpl.Factory<P> {
|
||||||
|
protected final BindingSelection<T> bindingSelection;
|
||||||
|
|
||||||
|
RealOptionalBinderProviderWithDependencies(BindingSelection<T> bindingSelection) {
|
||||||
|
// We need delayed initialization so we can detect jit bindings created by other bindings
|
||||||
|
// while not also creating jit bindings ourselves. This ensures we only pick up user bindings
|
||||||
|
// if the binding would have existed in the injector statically.
|
||||||
|
super(InitializationTiming.DELAYED);
|
||||||
|
this.bindingSelection = bindingSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
|
bindingSelection.initialize(injector);
|
||||||
|
doInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the factory. BindingSelection is guaranteed to be initialized at this point and
|
||||||
|
* this will be called prior to any provisioning.
|
||||||
|
*/
|
||||||
|
abstract void doInitialize();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj != null
|
||||||
|
&& this.getClass() == obj.getClass()
|
||||||
|
&& bindingSelection.equals(
|
||||||
|
((RealOptionalBinderProviderWithDependencies<?, ?>) obj).bindingSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return bindingSelection.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class DefaultImpl extends BaseAnnotation implements Default {
|
||||||
|
public DefaultImpl(String value) {
|
||||||
|
super(Default.class, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class ActualImpl extends BaseAnnotation implements Actual {
|
||||||
|
public ActualImpl(String value) {
|
||||||
|
super(Actual.class, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract static class BaseAnnotation implements Serializable, Annotation {
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
private final Class<? extends Annotation> clazz;
|
||||||
|
|
||||||
|
BaseAnnotation(Class<? extends Annotation> clazz, String value) {
|
||||||
|
this.clazz = checkNotNull(clazz, "clazz");
|
||||||
|
this.value = checkNotNull(value, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// This is specified in java.lang.Annotation.
|
||||||
|
return (127 * "value".hashCode()) ^ value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
// We check against each annotation type instead of BaseAnnotation
|
||||||
|
// so that we can compare against generated annotation implementations.
|
||||||
|
if (o instanceof Actual && clazz == Actual.class) {
|
||||||
|
Actual other = (Actual) o;
|
||||||
|
return value.equals(other.value());
|
||||||
|
} else if (o instanceof Default && clazz == Default.class) {
|
||||||
|
Default other = (Default) o;
|
||||||
|
return value.equals(other.value());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "@" + clazz.getName() + (value.isEmpty() ? "" : "(value=" + value + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Annotation> annotationType() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -184,8 +184,7 @@ public abstract class Scoping {
|
||||||
|
|
||||||
Scope scope = scoping.getScopeInstance();
|
Scope scope = scoping.getScopeInstance();
|
||||||
|
|
||||||
Provider<T> scoped
|
Provider<T> scoped = scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
|
||||||
= scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
|
|
||||||
return new InternalFactoryToProviderAdapter<T>(scoped, source);
|
return new InternalFactoryToProviderAdapter<T>(scoped, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,35 +10,38 @@ import java.lang.reflect.Field;
|
||||||
* Sets an injectable field.
|
* Sets an injectable field.
|
||||||
*/
|
*/
|
||||||
final class SingleFieldInjector implements SingleMemberInjector {
|
final class SingleFieldInjector implements SingleMemberInjector {
|
||||||
final Field field;
|
|
||||||
final InjectionPoint injectionPoint;
|
|
||||||
final Dependency<?> dependency;
|
|
||||||
final BindingImpl<?> binding;
|
|
||||||
|
|
||||||
public SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors)
|
private final Field field;
|
||||||
|
|
||||||
|
private final InjectionPoint injectionPoint;
|
||||||
|
|
||||||
|
private final Dependency<?> dependency;
|
||||||
|
|
||||||
|
private final BindingImpl<?> binding;
|
||||||
|
|
||||||
|
SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
this.injectionPoint = injectionPoint;
|
this.injectionPoint = injectionPoint;
|
||||||
this.field = (Field) injectionPoint.getMember();
|
this.field = (Field) injectionPoint.getMember();
|
||||||
this.dependency = injectionPoint.getDependencies().get(0);
|
this.dependency = injectionPoint.getDependencies().get(0);
|
||||||
|
|
||||||
// Ewwwww...
|
// Ewwwww...
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
|
binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public InjectionPoint getInjectionPoint() {
|
public InjectionPoint getInjectionPoint() {
|
||||||
return injectionPoint;
|
return injectionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Errors errors, InternalContext context, Object o) {
|
@Override
|
||||||
errors = errors.withSource(dependency);
|
public void inject(InternalContext context, Object o) throws InternalProvisionException {
|
||||||
|
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
|
||||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
|
||||||
try {
|
try {
|
||||||
Object value = binding.getInternalFactory().get(errors, context, dependency, false);
|
Object value = binding.getInternalFactory().get(context, dependency, false);
|
||||||
field.set(o, value);
|
field.set(o, value);
|
||||||
} catch (ErrorsException e) {
|
} catch (InternalProvisionException e) {
|
||||||
errors.withSource(injectionPoint).merge(e.getErrors());
|
throw e.addSource(dependency);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import com.google.inject.spi.InjectionPoint;
|
||||||
* Injects a field or method of a given object.
|
* Injects a field or method of a given object.
|
||||||
*/
|
*/
|
||||||
interface SingleMemberInjector {
|
interface SingleMemberInjector {
|
||||||
void inject(Errors errors, InternalContext context, Object o);
|
void inject(InternalContext context, Object o) throws InternalProvisionException;
|
||||||
|
|
||||||
InjectionPoint getInjectionPoint();
|
InjectionPoint getInjectionPoint();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,37 +29,23 @@ final class SingleMethodInjector implements SingleMemberInjector {
|
||||||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
return method::invoke;
|
||||||
return new MethodInvoker() {
|
|
||||||
public Object invoke(Object target, Object... parameters)
|
|
||||||
throws IllegalAccessException, InvocationTargetException {
|
|
||||||
return method.invoke(target, parameters);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InjectionPoint getInjectionPoint() {
|
public InjectionPoint getInjectionPoint() {
|
||||||
return injectionPoint;
|
return injectionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Errors errors, InternalContext context, Object o) {
|
@Override
|
||||||
Object[] parameters;
|
public void inject(InternalContext context, Object o) throws InternalProvisionException {
|
||||||
try {
|
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
|
||||||
parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
|
||||||
} catch (ErrorsException e) {
|
|
||||||
errors.merge(e.getErrors());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
methodInvoker.invoke(o, parameters);
|
methodInvoker.invoke(o, parameters);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||||
} catch (InvocationTargetException userException) {
|
} catch (InvocationTargetException userException) {
|
||||||
Throwable cause = userException.getCause() != null
|
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
|
||||||
? userException.getCause()
|
throw InternalProvisionException.errorInjectingMethod(cause).addSource(injectionPoint);
|
||||||
: userException;
|
|
||||||
errors.withSource(injectionPoint).errorInjectingMethod(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,48 +6,47 @@ import com.google.inject.spi.Dependency;
|
||||||
* Resolves a single parameter, to be used in a constructor or method invocation.
|
* Resolves a single parameter, to be used in a constructor or method invocation.
|
||||||
*/
|
*/
|
||||||
final class SingleParameterInjector<T> {
|
final class SingleParameterInjector<T> {
|
||||||
|
|
||||||
private static final Object[] NO_ARGUMENTS = {};
|
private static final Object[] NO_ARGUMENTS = {};
|
||||||
|
|
||||||
private final Dependency<T> dependency;
|
private final Dependency<T> dependency;
|
||||||
private final BindingImpl<? extends T> binding;
|
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
private final InternalFactory<? extends T> factory;
|
||||||
|
|
||||||
SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends T> binding) {
|
SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends T> binding) {
|
||||||
this.dependency = dependency;
|
this.dependency = dependency;
|
||||||
this.binding = binding;
|
this.source = binding.getSource();
|
||||||
|
this.factory = binding.getInternalFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of parameter values.
|
* Returns an array of parameter values.
|
||||||
*/
|
*/
|
||||||
static Object[] getAll(Errors errors, InternalContext context,
|
static Object[] getAll(InternalContext context,
|
||||||
SingleParameterInjector<?>[] parameterInjectors) throws ErrorsException {
|
SingleParameterInjector<?>[] parameterInjectors) throws InternalProvisionException {
|
||||||
if (parameterInjectors == null) {
|
if (parameterInjectors == null) {
|
||||||
return NO_ARGUMENTS;
|
return NO_ARGUMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int numErrorsBefore = errors.size();
|
|
||||||
|
|
||||||
int size = parameterInjectors.length;
|
int size = parameterInjectors.length;
|
||||||
Object[] parameters = new Object[size];
|
Object[] parameters = new Object[size];
|
||||||
|
|
||||||
// optimization: use manual for/each to save allocating an iterator here
|
// optimization: use manual for/each to save allocating an iterator here
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
SingleParameterInjector<?> parameterInjector = parameterInjectors[i];
|
parameters[i] = parameterInjectors[i].inject(context);
|
||||||
try {
|
|
||||||
parameters[i] = parameterInjector.inject(errors, context);
|
|
||||||
} catch (ErrorsException e) {
|
|
||||||
errors.merge(e.getErrors());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
errors.throwIfNewErrors(numErrorsBefore);
|
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private T inject(Errors errors, InternalContext context) throws ErrorsException {
|
T inject(InternalContext context) throws InternalProvisionException {
|
||||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
Dependency<T> localDependency = dependency;
|
||||||
|
Dependency<?> previous = context.pushDependency(localDependency, source);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return binding.getInternalFactory().get(errors.withSource(dependency), context, dependency, false);
|
return factory.get(context, localDependency, false);
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(localDependency);
|
||||||
} finally {
|
} finally {
|
||||||
context.popStateAndSetDependency(previous);
|
context.popStateAndSetDependency(previous);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue