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
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
- openjdk11
|
||||
|
|
|
@ -17,7 +17,7 @@ original [Google Guice Core Library](https://github.com/google/guice):
|
|||
- code is conforming to JRE `compact1` profile
|
||||
- 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 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
|
||||
- target of compilation is Java 8, no support for Java 6/7
|
||||
- introduced Gradle as build system
|
||||
|
|
190
build.gradle
190
build.gradle
|
@ -1,50 +1,43 @@
|
|||
|
||||
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: 'maven'
|
||||
apply plugin: 'signing'
|
||||
apply plugin: 'findbugs'
|
||||
apply plugin: 'pmd'
|
||||
apply plugin: 'checkstyle'
|
||||
apply plugin: "jacoco"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
wagon
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "javax.inject:javax.inject:1"
|
||||
compile 'org.xbib:jsr-305:1.0.0'
|
||||
compile "com.google.guava:guava:21.0"
|
||||
testCompile "junit:junit:4.12"
|
||||
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.7"
|
||||
testCompile "org.apache.logging.log4j:log4j-core:2.7"
|
||||
testCompile "javax.inject:javax.inject-tck:1"
|
||||
testCompile "com.google.guava:guava-testlib:21.0"
|
||||
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
|
||||
compile "org.xbib:javax-inject:${project.property('javax-inject.version')}"
|
||||
compile "org.xbib:guava:${project.property('guava.version')}"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-api:${project.property('junit.version')}"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-engine:${project.property('junit.version')}"
|
||||
testCompile "org.junit.vintage:junit-vintage-engine:${project.property('junit.version')}"
|
||||
testCompile "junit:junit:${project.property('junit4.version')}"
|
||||
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
|
||||
testCompile "com.google.guava:guava-testlib:28.1-jre"
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
compileJava {
|
||||
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) {
|
||||
options.compilerArgs << "-Xlint:all" << "-profile" << "compact1"
|
||||
options.compilerArgs << "-Xlint:all,-fallthrough"
|
||||
}
|
||||
|
||||
test {
|
||||
exclude '*$*'
|
||||
exclude '**/ErrorHandlingTest*'
|
||||
exclude '**/OSGiContainerTest*'
|
||||
exclude '**/ScopesTest*'
|
||||
exclude '**/TypeConversionTest*'
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
showStandardStreams = false
|
||||
exceptionFormat = 'full'
|
||||
|
@ -55,18 +48,145 @@ task sourcesJar(type: Jar, dependsOn: classes) {
|
|||
classifier 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier 'javadoc'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives sourcesJar, javadocJar
|
||||
}
|
||||
if (project.hasProperty('signing.keyId')) {
|
||||
signing {
|
||||
sign configurations.archives
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
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"
|
||||
apply from: "${rootProject.projectDir}/gradle/sonarqube.gradle"
|
||||
|
||||
nexusStaging {
|
||||
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
|
||||
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
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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
|
||||
|
||||
#
|
||||
# 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
|
||||
|
@ -28,16 +44,16 @@ APP_NAME="Gradle"
|
|||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
die () {
|
||||
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\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
@ -155,7 +171,7 @@ if $cygwin ; then
|
|||
fi
|
||||
|
||||
# Escape application args
|
||||
save ( ) {
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
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
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
|
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
|
|
@ -36,6 +36,7 @@ public abstract class AbstractModule implements Module {
|
|||
|
||||
Binder binder;
|
||||
|
||||
@Override
|
||||
public final synchronized void configure(Binder builder) {
|
||||
checkState(this.binder == null, "Re-entry is not allowed.");
|
||||
|
||||
|
@ -63,8 +64,7 @@ public abstract class AbstractModule implements Module {
|
|||
/**
|
||||
* @see Binder#bindScope(Class, Scope)
|
||||
*/
|
||||
protected void bindScope(Class<? extends Annotation> scopeAnnotation,
|
||||
Scope scope) {
|
||||
protected void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
|
||||
binder().bindScope(scopeAnnotation, scope);
|
||||
}
|
||||
|
||||
|
|
|
@ -367,7 +367,7 @@ public interface Binder {
|
|||
* their clients.
|
||||
* @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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
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 java.util.Collection;
|
||||
|
@ -24,7 +24,7 @@ public final class ConfigurationException extends RuntimeException {
|
|||
*/
|
||||
public ConfigurationException(Iterable<Message> 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
|
||||
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;
|
||||
|
||||
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 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
|
||||
* errors. Clients should catch this exception, log it, and stop execution.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CreationException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
private final ImmutableSet<Message> messages;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,7 @@ public class CreationException extends RuntimeException {
|
|||
public CreationException(Collection<Message> messages) {
|
||||
this.messages = ImmutableSet.copyOf(messages);
|
||||
checkArgument(!this.messages.isEmpty());
|
||||
initCause(Errors.getOnlyCause(this.messages));
|
||||
initCause(Messages.getOnlyCause(this.messages));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,6 +35,6 @@ public class CreationException extends RuntimeException {
|
|||
|
||||
@Override
|
||||
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
|
||||
* construction
|
||||
*/
|
||||
public static Injector createInjector(Stage stage,
|
||||
Iterable<? extends Module> modules) {
|
||||
return new InternalInjectorCreator()
|
||||
.stage(stage)
|
||||
.addModules(modules)
|
||||
.build();
|
||||
public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) {
|
||||
return new InternalInjectorCreator().stage(stage).addModules(modules).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
import com.google.inject.spi.Element;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.TypeConverterBinding;
|
||||
|
||||
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.
|
||||
*/
|
||||
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 TypeLiteral<T> typeLiteral;
|
||||
|
||||
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.
|
||||
|
@ -60,7 +65,6 @@ public class Key<T> {
|
|||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,7 +86,6 @@ public class Key<T> {
|
|||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,7 +105,6 @@ public class Key<T> {
|
|||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,7 +115,6 @@ public class Key<T> {
|
|||
this.annotationStrategy = annotationStrategy;
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,14 +124,12 @@ public class Key<T> {
|
|||
this.annotationStrategy = annotationStrategy;
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for an injection type and an annotation strategy.
|
||||
*/
|
||||
static <T> Key<T> get(Class<T> type,
|
||||
AnnotationStrategy annotationStrategy) {
|
||||
static <T> Key<T> get(Class<T> type, AnnotationStrategy 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.
|
||||
*/
|
||||
public static <T> Key<T> get(Class<T> type,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
public static <T> Key<T> get(Class<T> type, Class<? extends Annotation> 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.
|
||||
*/
|
||||
public static Key<?> get(Type type,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
public static Key<?> get(Type type, Class<? extends Annotation> 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.
|
||||
*/
|
||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Class<? extends Annotation> annotationType) {
|
||||
return new Key<T>(typeLiteral, strategyFor(annotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for an injection type and an annotation.
|
||||
*/
|
||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
|
||||
Annotation annotation) {
|
||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Annotation annotation) {
|
||||
return new Key<T>(typeLiteral, strategyFor(annotation));
|
||||
}
|
||||
|
||||
|
@ -253,20 +248,6 @@ public class Key<T> {
|
|||
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.
|
||||
*/
|
||||
|
@ -333,7 +314,14 @@ public class Key<T> {
|
|||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
|
||||
static enum NullAnnotationStrategy implements AnnotationStrategy {
|
||||
enum NullAnnotationStrategy implements AnnotationStrategy {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public boolean hasAttributes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationStrategy withoutAttributes() {
|
||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return null;
|
||||
}
|
||||
|
@ -401,6 +393,7 @@ public class Key<T> {
|
|||
}
|
||||
|
||||
interface AnnotationStrategy {
|
||||
|
||||
Annotation getAnnotation();
|
||||
|
||||
Class<? extends Annotation> getAnnotationType();
|
||||
|
@ -419,18 +412,22 @@ public class Key<T> {
|
|||
this.annotation = checkNotNull(annotation, "annotation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttributes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationStrategy withoutAttributes() {
|
||||
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return annotation.annotationType();
|
||||
}
|
||||
|
@ -469,18 +466,22 @@ public class Key<T> {
|
|||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttributes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationStrategy withoutAttributes() {
|
||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
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 skipSources(Class... classesToSkip);
|
||||
PrivateBinder skipSources(Class<?>... classesToSkip);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject;
|
|||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
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
|
||||
* and throwables to describe why provision failed.
|
||||
*/
|
||||
@Override
|
||||
T get();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
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 java.util.Collection;
|
||||
|
@ -23,7 +23,7 @@ public final class ProvisionException extends RuntimeException {
|
|||
public ProvisionException(Iterable<Message> messages) {
|
||||
this.messages = ImmutableSet.copyOf(messages);
|
||||
checkArgument(!this.messages.isEmpty());
|
||||
initCause(Errors.getOnlyCause(this.messages));
|
||||
initCause(Messages.getOnlyCause(this.messages));
|
||||
}
|
||||
|
||||
public ProvisionException(String message, Throwable cause) {
|
||||
|
@ -44,6 +44,6 @@ public final class ProvisionException extends RuntimeException {
|
|||
|
||||
@Override
|
||||
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.
|
||||
*/
|
||||
public static final Scope NO_SCOPE = new Scope() {
|
||||
@Override
|
||||
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
||||
return unscoped;
|
||||
}
|
||||
|
@ -37,21 +38,26 @@ public class Scopes {
|
|||
return "Scopes.NO_SCOPE";
|
||||
}
|
||||
};
|
||||
|
||||
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
||||
= new BindingScopingVisitor<Boolean>() {
|
||||
= new BindingScopingVisitor<>() {
|
||||
@Override
|
||||
public Boolean visitNoScoping() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
||||
return scopeAnnotation == Singleton.class
|
||||
|| scopeAnnotation == javax.inject.Singleton.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScope(Scope scope) {
|
||||
return scope == Scopes.SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitEagerSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
@ -107,18 +113,22 @@ public class Scopes {
|
|||
final Class<? extends Annotation> scopeAnnotation) {
|
||||
do {
|
||||
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
||||
@Override
|
||||
public Boolean visitNoScoping() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
||||
return visitedAnnotation == scopeAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScope(Scope visitedScope) {
|
||||
return visitedScope == scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitEagerSingleton() {
|
||||
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
|
||||
while (true) {
|
||||
if (toResolve instanceof TypeVariable) {
|
||||
TypeVariable original = (TypeVariable) toResolve;
|
||||
TypeVariable<?> original = (TypeVariable) toResolve;
|
||||
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
||||
if (toResolve == original) {
|
||||
return toResolve;
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.google.inject.Provider;
|
|||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.internal.Errors;
|
||||
import com.google.inject.internal.ErrorsException;
|
||||
import com.google.inject.internal.Messages;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.HasDependencies;
|
||||
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) {
|
||||
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
|
||||
return new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
|
||||
}
|
||||
|
||||
@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>
|
||||
Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
|
||||
Class<F> factoryRawType = (Class<F>) factoryType.getRawType();
|
||||
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
|
||||
new Class[]{factoryRawType}, invocationHandler));
|
||||
new Class<?>[]{factoryRawType}, invocationHandler));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -96,7 +96,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
|||
|
||||
@Override
|
||||
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) "
|
||||
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) "
|
||||
+ "The exact offending deps are: {2}",
|
||||
new Object[]{factoryType, implementation, badDeps});
|
||||
new Object[] { factoryType, implementation, badDeps } );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -51,20 +51,17 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
|||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
ScopedBindingBuilder toProvider(
|
||||
Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
ScopedBindingBuilder toProvider(Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
|
||||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
ScopedBindingBuilder toProvider(
|
||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
ScopedBindingBuilder toProvider(TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
|
||||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
ScopedBindingBuilder toProvider(
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||
ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
<S extends T> ScopedBindingBuilder toConstructor(
|
||||
Constructor<S> constructor, TypeLiteral<? extends S> type);
|
||||
<S extends T> ScopedBindingBuilder toConstructor(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 static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
||||
public static final String SINGLE_INSTANCE_AND_SCOPE
|
||||
= "Setting the scope is not permitted when binding to a single instance.";
|
||||
public static final String SINGLE_INSTANCE_AND_SCOPE =
|
||||
"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 BINDING_TO_NULL = "Binding to null instances is not allowed. "
|
||||
+ "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
|
||||
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.";
|
||||
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
|
||||
public static final String ANNOTATION_ALREADY_SPECIFIED
|
||||
= "More than one annotation is specified for this binding.";
|
||||
public static final String ANNOTATION_ALREADY_SPECIFIED =
|
||||
"More than one annotation is specified for this binding.";
|
||||
|
||||
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
||||
protected final Binder binder;
|
||||
|
@ -37,7 +37,7 @@ public abstract class AbstractBindingBuilder<T> {
|
|||
this.binder = binder;
|
||||
this.elements = elements;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -57,8 +57,7 @@ public abstract class AbstractBindingBuilder<T> {
|
|||
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
||||
checkNotNull(annotationType, "annotationType");
|
||||
checkNotAnnotated();
|
||||
return setBinding(binding.withKey(
|
||||
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,8 +66,7 @@ public abstract class AbstractBindingBuilder<T> {
|
|||
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
||||
checkNotNull(annotation, "annotation");
|
||||
checkNotAnnotated();
|
||||
return setBinding(binding.withKey(
|
||||
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||
}
|
||||
|
||||
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
|
||||
// classes, but we can't easily block the whole package because of
|
||||
// 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,
|
||||
Binder.class,
|
||||
Binding.class,
|
||||
|
@ -93,7 +93,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
|||
*/
|
||||
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
|
||||
if (original instanceof ExposedBindingImpl) {
|
||||
ExposedBindingImpl exposed = (ExposedBindingImpl) original;
|
||||
ExposedBindingImpl<?> exposed = (ExposedBindingImpl) original;
|
||||
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
||||
return (exposedFrom == binding.getInjector());
|
||||
} else {
|
||||
|
@ -131,16 +131,28 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
|||
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
||||
}
|
||||
|
||||
protected void scheduleInitialization(final BindingImpl<?> binding) {
|
||||
bindingData.addUninitializedBinding(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Schedule initialization of this binding to occur immediately after all bindings have been
|
||||
* 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 {
|
||||
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.google.inject.internal;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Joiner.MapJoiner;
|
||||
|
@ -43,14 +45,17 @@ public class Annotations {
|
|||
return s.substring(1, s.length() - 1); // cut off brackets
|
||||
}
|
||||
};
|
||||
|
||||
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
|
||||
CacheBuilder.newBuilder().weakKeys().build(
|
||||
new CacheLoader<Class<? extends Annotation>, Annotation>() {
|
||||
CacheBuilder.newBuilder()
|
||||
.weakKeys()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Annotation load(Class<? extends Annotation> input) {
|
||||
return generateAnnotationImpl(input);
|
||||
}
|
||||
});
|
||||
|
||||
private static final AnnotationChecker scopeChecker = new AnnotationChecker(
|
||||
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
|
||||
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
|
||||
|
@ -90,21 +95,19 @@ public class Annotations {
|
|||
return annotationType.cast(Proxy.newProxyInstance(
|
||||
annotationType.getClassLoader(),
|
||||
new Class<?>[]{annotationType},
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
|
||||
String name = method.getName();
|
||||
if (name.equals("annotationType")) {
|
||||
(proxy, method, args) -> {
|
||||
String name = method.getName();
|
||||
switch (name) {
|
||||
case "annotationType":
|
||||
return annotationType;
|
||||
} else if (name.equals("toString")) {
|
||||
case "toString":
|
||||
return annotationToString(annotationType, members);
|
||||
} else if (name.equals("hashCode")) {
|
||||
case "hashCode":
|
||||
return annotationHashCode(annotationType, members);
|
||||
} else if (name.equals("equals")) {
|
||||
case "equals":
|
||||
return annotationEquals(annotationType, members, args[0]);
|
||||
} else {
|
||||
default:
|
||||
return members.get(name);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -207,6 +210,30 @@ public class Annotations {
|
|||
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) {
|
||||
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.
|
||||
*/
|
||||
public static Annotation findBindingAnnotation(
|
||||
Errors errors, Member member, Annotation[] annotations) {
|
||||
public static Annotation findBindingAnnotation(Errors errors, Member member, Annotation[] annotations) {
|
||||
Annotation found = null;
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -303,7 +347,8 @@ public class Annotations {
|
|||
* Returns true if the given class has one of the desired annotations.
|
||||
*/
|
||||
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
|
||||
new CacheLoader<Class<? extends Annotation>, Boolean>() {
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public Boolean load(Class<? extends Annotation> annotationType) {
|
||||
for (Annotation annotation : annotationType.getAnnotations()) {
|
||||
if (annotationTypes.contains(annotation.annotationType())) {
|
||||
|
@ -314,8 +359,8 @@ public class Annotations {
|
|||
}
|
||||
};
|
||||
|
||||
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
|
||||
.build(hasAnnotations);
|
||||
final LoadingCache<Class<? extends Annotation>, Boolean> cache =
|
||||
CacheBuilder.newBuilder().weakKeys().build(hasAnnotations);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
||||
annotatedWithInternal(annotationType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
||||
annotatedWithInternal(annotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
||||
return to(Key.get(implementation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
||||
return to(Key.get(implementation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
||||
checkNotNull(linkedKey, "linkedKey");
|
||||
checkNotTargetted();
|
||||
|
@ -56,6 +61,7 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toInstance(T instance) {
|
||||
checkNotTargetted();
|
||||
|
||||
|
@ -79,10 +85,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
||||
return toProvider((javax.inject.Provider<T>) provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
||||
checkNotNull(provider, "provider");
|
||||
checkNotTargetted();
|
||||
|
@ -102,16 +110,19 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(
|
||||
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||
return toProvider(Key.get(providerType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(
|
||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||
return toProvider(Key.get(providerType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||
checkNotNull(providerKey, "providerKey");
|
||||
|
@ -123,10 +134,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
||||
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
||||
TypeLiteral<? extends S> type) {
|
||||
checkNotNull(constructor, "constructor");
|
||||
|
|
|
@ -34,14 +34,17 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
|||
this.scoping = scoping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<T> getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<T> getProvider() {
|
||||
if (provider == null) {
|
||||
if (injector == null) {
|
||||
|
@ -69,10 +72,12 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
|||
return this instanceof InstanceBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
||||
return scoping.acceptVisitor(visitor);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
return true;
|
||||
}
|
||||
|
||||
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) {
|
||||
return command.acceptTargetVisitor(new Processor<>((BindingImpl<T>) command) {
|
||||
@Override
|
||||
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
|
@ -72,13 +72,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
T instance = binding.getInstance();
|
||||
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
|
||||
// the processor was constructed w/ it
|
||||
Initializable<T> ref = initializer.requestInjection(
|
||||
Initializable<T> ref = initializer.requestInjection(
|
||||
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
||||
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
|
||||
instance));
|
||||
ConstantFactory<? extends T> factory = new ConstantFactory<>(ref);
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new InstanceBindingImpl<>(injector, key, source,
|
||||
scopedFactory, injectionPoints, instance));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -86,18 +86,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
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();
|
||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
||||
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
||||
injector, provider, null, source, injectionPoints);
|
||||
initializer.requestInjection(injector, provider, null, source, injectionPoints);
|
||||
// always visited with Binding<T>
|
||||
@SuppressWarnings("unchecked")
|
||||
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
|
||||
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<>(
|
||||
initializable, source,
|
||||
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
|
||||
InternalFactory<? extends T> scopedFactory = Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new ProviderInstanceBindingImpl<>(injector, key, source, scopedFactory, scoping,
|
||||
provider, injectionPoints));
|
||||
return true;
|
||||
}
|
||||
|
@ -108,13 +112,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
|
||||
// always visited with Binding<T>
|
||||
@SuppressWarnings("unchecked")
|
||||
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
|
||||
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<>(
|
||||
injector, providerKey, source,
|
||||
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
||||
bindingData.addCreationListener(boundProviderFactory);
|
||||
InternalFactory<? extends T> scopedFactory = Scoping.scope(
|
||||
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
|
||||
putBinding(new LinkedProviderBindingImpl<T>(
|
||||
key, injector, boundProviderFactory, source, scoping);
|
||||
putBinding(new LinkedProviderBindingImpl<>(
|
||||
injector, key, source, scopedFactory, scoping, providerKey));
|
||||
return true;
|
||||
}
|
||||
|
@ -126,13 +130,36 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
if (key.equals(linkedKey)) {
|
||||
errors.recursiveBinding();
|
||||
}
|
||||
|
||||
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
|
||||
FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
|
||||
bindingData.addCreationListener(factory);
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(
|
||||
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new LinkedBindingImpl<>(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;
|
||||
}
|
||||
|
||||
|
@ -172,9 +199,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
}
|
||||
|
||||
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);
|
||||
putBinding(new ExposedBindingImpl<T>(
|
||||
putBinding(new ExposedBindingImpl<>(
|
||||
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,17 @@ import com.google.inject.spi.Dependency;
|
|||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Delegates to a custom factory which is also bound in the injector.
|
||||
*/
|
||||
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 InjectorImpl injector;
|
||||
|
||||
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
||||
|
||||
BoundProviderFactory(
|
||||
|
@ -24,11 +25,12 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
|
|||
Object source,
|
||||
ProvisionListenerStackCallback<T> provisionCallback) {
|
||||
super(source);
|
||||
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
|
||||
this.provisionCallback = provisionCallback;
|
||||
this.injector = injector;
|
||||
this.providerKey = providerKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(Errors errors) {
|
||||
try {
|
||||
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)
|
||||
throws ErrorsException {
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
context.pushState(providerKey, source);
|
||||
try {
|
||||
errors = errors.withSource(providerKey);
|
||||
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
|
||||
return circularGet(provider, errors, context, dependency, provisionCallback);
|
||||
javax.inject.Provider<? extends T> provider = providerFactory.get(context, dependency, true);
|
||||
return circularGet(provider, context, dependency, provisionCallback);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(providerKey);
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||
try {
|
||||
return super.provision(provider, errors, dependency, constructionContext);
|
||||
return super.provision(provider, dependency, constructionContext);
|
||||
} 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.ConstantBindingBuilder;
|
||||
import com.google.inject.spi.Element;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
|
@ -24,56 +23,69 @@ public final class ConstantBindingBuilderImpl<T>
|
|||
super(binder, elements, source, (Key<T>) NULL_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
||||
annotatedWithInternal(annotationType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
||||
annotatedWithInternal(annotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final String value) {
|
||||
toConstant(String.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final int value) {
|
||||
toConstant(Integer.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final long value) {
|
||||
toConstant(Long.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final boolean value) {
|
||||
toConstant(Boolean.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final double value) {
|
||||
toConstant(Double.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final float value) {
|
||||
toConstant(Float.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final short value) {
|
||||
toConstant(Short.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final char value) {
|
||||
toConstant(Character.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final byte value) {
|
||||
toConstant(Byte.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final Class<?> value) {
|
||||
toConstant(Class.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> void to(final E value) {
|
||||
toConstant(value.getDeclaringClass(), value);
|
||||
}
|
||||
|
@ -105,7 +117,7 @@ public final class ConstantBindingBuilderImpl<T>
|
|||
}
|
||||
|
||||
setBinding(new InstanceBindingImpl<T>(
|
||||
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT));
|
||||
base.getSource(), key, base.getScoping(), ImmutableSet.of(), instanceAsT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,15 +7,17 @@ final class ConstantFactory<T> implements InternalFactory<T> {
|
|||
|
||||
private final Initializable<T> initializable;
|
||||
|
||||
public ConstantFactory(Initializable<T> initializable) {
|
||||
ConstantFactory(Initializable<T> initializable) {
|
||||
this.initializable = initializable;
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
return initializable.get(errors);
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
return initializable.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(ConstantFactory.class)
|
||||
.add("value", initializable)
|
||||
|
|
|
@ -41,26 +41,26 @@ final class ConstructionContext<T> {
|
|||
invocationHandlers = null;
|
||||
}
|
||||
|
||||
public Object createProxy(Errors errors, InjectorOptions injectorOptions,
|
||||
Class<?> expectedType) throws ErrorsException {
|
||||
public Object createProxy(InjectorOptions injectorOptions,
|
||||
Class<?> expectedType) throws InternalProvisionException {
|
||||
if (injectorOptions.disableCircularProxies) {
|
||||
throw errors.circularProxiesDisabled(expectedType).toException();
|
||||
throw InternalProvisionException.circularDependenciesDisabled(expectedType);
|
||||
}
|
||||
if (!expectedType.isInterface()) {
|
||||
throw errors.cannotSatisfyCircularDependency(expectedType).toException();
|
||||
throw InternalProvisionException.cannotProxyClass(expectedType);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
||||
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
||||
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
||||
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||
new Class<?>[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||
}
|
||||
|
||||
public void setProxyDelegates(T delegate) {
|
||||
|
|
|
@ -26,10 +26,15 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
implements ConstructorBinding<T>, DelayedInitialize {
|
||||
|
||||
private final Factory<T> factory;
|
||||
|
||||
private final InjectionPoint constructorInjectionPoint;
|
||||
|
||||
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
|
||||
private ConstructorBindingImpl(InjectorImpl injector,
|
||||
Key<T> key,
|
||||
Object source,
|
||||
InternalFactory<? extends T> scopedFactory,
|
||||
Scoping scoping,
|
||||
Factory<T> factory,
|
||||
InjectionPoint constructorInjectionPoint) {
|
||||
super(injector, key, source, scopedFactory, scoping);
|
||||
this.factory = factory;
|
||||
|
@ -53,7 +58,10 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
* only succeed if retrieved from a linked binding
|
||||
*/
|
||||
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)
|
||||
throws ErrorsException {
|
||||
int numErrors = errors.size();
|
||||
|
@ -65,7 +73,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
|
||||
// We can't inject abstract classes.
|
||||
if (Modifier.isAbstract(rawType.getModifiers())) {
|
||||
errors.missingImplementation(key);
|
||||
errors.missingImplementationWithHint(key, injector);
|
||||
}
|
||||
|
||||
// Error: Inner class.
|
||||
|
@ -110,7 +118,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
/**
|
||||
* 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)
|
||||
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
|
||||
}
|
||||
|
@ -162,21 +170,25 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
return Dependency.forInjectionPoints(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
checkState(factory.constructorInjector != null, "not initialized");
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InjectionPoint getConstructor() {
|
||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<InjectionPoint> getInjectableMembers() {
|
||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||
return factory.constructorInjector.getInjectableMembers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
|
||||
.add(getConstructor())
|
||||
|
@ -241,18 +253,19 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
checkState(constructorInjector != null, "Constructor not ready");
|
||||
|
||||
if (failIfNotLinked && !linked) {
|
||||
throw errors.jitDisabled(key).toException();
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
ConstructorInjector<T> localInjector = constructorInjector;
|
||||
if (localInjector == null) {
|
||||
throw new IllegalStateException("Constructor not ready");
|
||||
}
|
||||
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
|
||||
// client needs), but it should be OK in practice thanks to the wonders of erasure.
|
||||
return (T) constructorInjector.construct(errors, context,
|
||||
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
|
||||
return (T) localInjector.construct(context, dependency, provisionCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject.internal;
|
|||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -40,37 +41,39 @@ final class ConstructorInjector<T> {
|
|||
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
||||
* it may return a proxy.
|
||||
*/
|
||||
Object construct(final Errors errors, final InternalContext context,
|
||||
Class<?> expectedType,
|
||||
Object construct(final InternalContext context,
|
||||
Dependency<?> dependency,
|
||||
ProvisionListenerStackCallback<T> provisionCallback)
|
||||
throws ErrorsException {
|
||||
throws InternalProvisionException {
|
||||
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||
|
||||
// We have a circular reference between constructors. Return a proxy.
|
||||
if (constructionContext.isConstructing()) {
|
||||
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
||||
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,
|
||||
// return the same instance. This prevents infinite loops.
|
||||
T t = constructionContext.getCurrentReference();
|
||||
if (t != null) {
|
||||
return t;
|
||||
if (context.getInjectorOptions().disableCircularProxies) {
|
||||
throw InternalProvisionException.circularDependenciesDisabled(
|
||||
dependency.getKey().getTypeLiteral().getRawType());
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
constructionContext.startConstruction();
|
||||
try {
|
||||
// Optimization: Don't go through the callback stack if we have no listeners.
|
||||
if (!provisionCallback.hasListeners()) {
|
||||
return provision(errors, context, constructionContext);
|
||||
if (provisionCallback == null) {
|
||||
return provision(context, constructionContext);
|
||||
} else {
|
||||
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
||||
public T call() throws ErrorsException {
|
||||
return provision(errors, context, constructionContext);
|
||||
}
|
||||
});
|
||||
return provisionCallback.provision(context, () ->
|
||||
provision(context, constructionContext));
|
||||
}
|
||||
} finally {
|
||||
constructionContext.finishConstruction();
|
||||
|
@ -80,12 +83,12 @@ final class ConstructorInjector<T> {
|
|||
/**
|
||||
* Provisions a new T.
|
||||
*/
|
||||
private T provision(Errors errors, InternalContext context,
|
||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||
private T provision(InternalContext context, ConstructionContext<T> constructionContext)
|
||||
throws InternalProvisionException {
|
||||
try {
|
||||
T t;
|
||||
try {
|
||||
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
||||
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
|
||||
t = constructionProxy.newInstance(parameters);
|
||||
constructionContext.setProxyDelegates(t);
|
||||
} finally {
|
||||
|
@ -94,17 +97,17 @@ final class ConstructorInjector<T> {
|
|||
|
||||
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
||||
constructionContext.setCurrentReference(t);
|
||||
|
||||
membersInjector.injectMembers(t, errors, context, false);
|
||||
membersInjector.notifyListeners(t, errors);
|
||||
MembersInjectorImpl<T> localMembersInjector = membersInjector;
|
||||
localMembersInjector.injectMembers(t, context, false);
|
||||
localMembersInjector.notifyListeners(t);
|
||||
|
||||
return t;
|
||||
} catch (InvocationTargetException userException) {
|
||||
Throwable cause = userException.getCause() != null
|
||||
? userException.getCause()
|
||||
: userException;
|
||||
throw errors.withSource(constructionProxy.getInjectionPoint())
|
||||
.errorInjectingConstructor(cause).toException();
|
||||
throw InternalProvisionException.errorInjectingConstructor(cause)
|
||||
.addSource(constructionProxy.getInjectionPoint());
|
||||
} finally {
|
||||
constructionContext.removeCurrentReference();
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import com.google.inject.spi.InjectionPoint;
|
|||
final class ConstructorInjectorStore {
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
|
||||
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() {
|
||||
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache =
|
||||
new FailableCache<>() {
|
||||
@Override
|
||||
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
||||
throws ErrorsException {
|
||||
|
@ -46,12 +46,12 @@ final class ConstructorInjectorStore {
|
|||
throws ErrorsException {
|
||||
int numErrorsBefore = errors.size();
|
||||
|
||||
SingleParameterInjector<?>[] constructorParameterInjectors
|
||||
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||
SingleParameterInjector<?>[] constructorParameterInjectors =
|
||||
injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||
|
||||
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
||||
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
|
||||
.get(injectionPoint.getDeclaringType(), errors);
|
||||
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)
|
||||
injector.membersInjectorStore.get(injectionPoint.getDeclaringType(), errors);
|
||||
|
||||
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;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
|
@ -12,7 +11,6 @@ import com.google.common.collect.Multimaps;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
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 cycle is detected performance is O(singleton locks).
|
||||
*/
|
||||
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle();
|
||||
ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle();
|
||||
|
||||
/**
|
||||
* Unlocks previously locked lock.
|
||||
|
@ -82,7 +80,7 @@ interface CycleDetectingLock<ID> {
|
|||
*
|
||||
* Guarded by {@code this}.
|
||||
*/
|
||||
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread =
|
||||
private static final Multimap<Thread, ReentrantCycleDetectingLock<?>> locksOwnedByThread =
|
||||
LinkedHashMultimap.create();
|
||||
/**
|
||||
* Specifies lock that thread is currently waiting on to own it.
|
||||
|
@ -100,22 +98,22 @@ interface CycleDetectingLock<ID> {
|
|||
*
|
||||
* 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
|
||||
* 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) {
|
||||
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock());
|
||||
CycleDetectingLock<ID> create(ID userLockId) {
|
||||
return new ReentrantCycleDetectingLock<>(this, userLockId, new ReentrantLock());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -125,50 +123,56 @@ interface CycleDetectingLock<ID> {
|
|||
* User id for this lock.
|
||||
*/
|
||||
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.
|
||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||
*/
|
||||
private Long lockOwnerThreadId = null;
|
||||
private Thread lockOwnerThread = null;
|
||||
/**
|
||||
* Number of times that thread owned this lock.
|
||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||
*/
|
||||
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.lockImplementation = Preconditions.checkNotNull(
|
||||
lockImplementation, "lockImplementation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
synchronized (CycleDetectingLockFactory.this) {
|
||||
public ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle() {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
synchronized (CycleDetectingLockFactory.class) {
|
||||
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()) {
|
||||
// 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
|
||||
return locksInCycle;
|
||||
}
|
||||
|
||||
lockThreadIsWaitingOn.put(currentThreadId, this);
|
||||
}
|
||||
|
||||
// this may be blocking, but we don't expect it to cause a deadlock
|
||||
lockImplementation.lock();
|
||||
|
||||
synchronized (CycleDetectingLockFactory.this) {
|
||||
synchronized (CycleDetectingLockFactory.class) {
|
||||
// current thread is no longer waiting on this lock
|
||||
lockThreadIsWaitingOn.remove(currentThreadId);
|
||||
lockThreadIsWaitingOn.remove(currentThread);
|
||||
checkState();
|
||||
|
||||
// mark it as owned by us
|
||||
lockOwnerThreadId = currentThreadId;
|
||||
lockOwnerThread = currentThread;
|
||||
lockReentranceCount++;
|
||||
// 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
|
||||
return ImmutableListMultimap.of();
|
||||
|
@ -176,12 +180,12 @@ interface CycleDetectingLock<ID> {
|
|||
|
||||
@Override
|
||||
public void unlock() {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
synchronized (CycleDetectingLockFactory.this) {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
synchronized (CycleDetectingLockFactory.class) {
|
||||
checkState();
|
||||
Preconditions.checkState(lockOwnerThreadId != null,
|
||||
Preconditions.checkState(lockOwnerThread != null,
|
||||
"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");
|
||||
|
||||
// releasing underlying lock
|
||||
|
@ -191,12 +195,12 @@ interface CycleDetectingLock<ID> {
|
|||
lockReentranceCount--;
|
||||
if (lockReentranceCount == 0) {
|
||||
// we no longer own this lock
|
||||
lockOwnerThreadId = null;
|
||||
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this),
|
||||
lockOwnerThread = null;
|
||||
Preconditions.checkState(locksOwnedByThread.remove(currentThread, this),
|
||||
"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
|
||||
locksOwnedByThread.removeAll(currentThreadId);
|
||||
locksOwnedByThread.removeAll(currentThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,14 +210,14 @@ interface CycleDetectingLock<ID> {
|
|||
* Check consistency of an internal state.
|
||||
*/
|
||||
void checkState() throws IllegalStateException {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId),
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThread),
|
||||
"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
|
||||
Preconditions.checkState(lockReentranceCount >= 0,
|
||||
"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 "
|
||||
+ "ownership status do not match");
|
||||
} else {
|
||||
|
@ -233,74 +237,80 @@ interface CycleDetectingLock<ID> {
|
|||
*
|
||||
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
||||
*/
|
||||
private ListMultimap<Long, ID> detectPotentialLocksCycle() {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) {
|
||||
private ListMultimap<Thread, ID> detectPotentialLocksCycle() {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
if (lockOwnerThread == null || lockOwnerThread == currentThread) {
|
||||
// if nobody owns this lock, lock cycle is impossible
|
||||
// if a current thread owns this lock, we let Guice to handle it
|
||||
return ImmutableListMultimap.of();
|
||||
}
|
||||
|
||||
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||
new LinkedHashMap<Long, Collection<ID>>(),
|
||||
new Supplier<List<ID>>() {
|
||||
@Override
|
||||
public List<ID> get() {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
});
|
||||
ListMultimap<Thread, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||
new LinkedHashMap<>(), Lists::newArrayList);
|
||||
// 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
|
||||
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) {
|
||||
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId;
|
||||
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThread != null) {
|
||||
Thread threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThread;
|
||||
// in case locks cycle exists lock we're waiting for is part of it
|
||||
potentialLocksCycle.putAll(threadOwnerThreadWaits,
|
||||
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn));
|
||||
|
||||
if (threadOwnerThreadWaits == currentThreadId) {
|
||||
lockOwnerWaitingOn =
|
||||
addAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn, potentialLocksCycle);
|
||||
if (threadOwnerThreadWaits == currentThread) {
|
||||
// owner thread depends on current thread, cycle detected
|
||||
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
|
||||
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) {
|
||||
List<ID> ids = Lists.newArrayList();
|
||||
private ReentrantCycleDetectingLock<?> addAllLockIdsAfter(
|
||||
Thread thread,
|
||||
ReentrantCycleDetectingLock<?> lock,
|
||||
ListMultimap<Thread, ID> potentialLocksCycle) {
|
||||
boolean found = false;
|
||||
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId);
|
||||
Collection<ReentrantCycleDetectingLock<?>> ownedLocks = locksOwnedByThread.get(thread);
|
||||
Preconditions.checkNotNull(ownedLocks,
|
||||
"Internal error: No locks were found taken by a thread");
|
||||
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) {
|
||||
for (ReentrantCycleDetectingLock<?> ownedLock : ownedLocks) {
|
||||
if (ownedLock == lock) {
|
||||
found = true;
|
||||
}
|
||||
if (found) {
|
||||
ids.add(ownedLock.userLockId);
|
||||
if (found && ownedLock.lockFactory == this.lockFactory) {
|
||||
// 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 "
|
||||
+ "created a cycle that we detected");
|
||||
return ids;
|
||||
Preconditions.checkState(found,
|
||||
"Internal error: We can not find locks that created a cycle that we detected");
|
||||
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
|
||||
public String toString() {
|
||||
// copy is made to prevent a data race
|
||||
// no synchronization is used, potentially stale data, should be good enough
|
||||
Long localLockOwnerThreadId = this.lockOwnerThreadId;
|
||||
if (localLockOwnerThreadId != null) {
|
||||
return String.format("CycleDetectingLock[%s][locked by %s]",
|
||||
userLockId, localLockOwnerThreadId);
|
||||
Thread thread = this.lockOwnerThread;
|
||||
if (thread != null) {
|
||||
return String.format("%s[%s][locked by %s]", super.toString(), userLockId, thread);
|
||||
} 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
|
||||
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
||||
|
||||
// Use FastConstructor if the constructor is public.
|
||||
if (Modifier.isPublic(constructor.getModifiers())) {
|
||||
Class<T> classToConstruct = constructor.getDeclaringClass();
|
||||
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
} else {
|
||||
if (!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) ||
|
||||
!Modifier.isPublic(constructor.getModifiers())) {
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
|
||||
|
@ -38,17 +33,17 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
|
|||
public T newInstance(Object... arguments) throws InvocationTargetException {
|
||||
try {
|
||||
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
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InjectionPoint getInjectionPoint() {
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
|
|
@ -32,13 +32,13 @@ final class DeferredLookups implements Lookups {
|
|||
}
|
||||
|
||||
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);
|
||||
return lookup.getProvider();
|
||||
}
|
||||
|
||||
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);
|
||||
return lookup.getMembersInjector();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.google.inject.multibindings;
|
||||
package com.google.inject.internal;
|
||||
|
||||
import com.google.inject.BindingAnnotation;
|
||||
|
||||
|
@ -25,7 +25,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
|
||||
enum Type {
|
||||
MAPBINDER,
|
||||
MULTIBINDER,
|
||||
OPTIONALBINDER;
|
||||
MULTIBINDER;
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
|||
: ImmutableSet.copyOf(injectionListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(MembersInjector<? super T> membersInjector) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
|
||||
|
@ -53,6 +54,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
|||
membersInjectors.add(membersInjector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(InjectionListener<? super T> injectionListener) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
|
||||
|
@ -63,36 +65,43 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
|||
injectionListeners.add(injectionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addError(String message, Object... arguments) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
errors.addMessage(message, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addError(Throwable t) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addError(Message message) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
errors.addMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Provider<T> getProvider(Key<T> key) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
return lookups.getProvider(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Provider<T> getProvider(Class<T> type) {
|
||||
return getProvider(Key.get(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
return lookups.getMembersInjector(typeLiteral);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(Class<T> 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.Lists;
|
||||
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.CreationException;
|
||||
import com.google.inject.Injector;
|
||||
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.Scope;
|
||||
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.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.ScopeBinding;
|
||||
import com.google.inject.spi.TypeConverterBinding;
|
||||
import com.google.inject.spi.TypeListenerBinding;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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
|
||||
|
@ -56,43 +48,40 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*/
|
||||
public final class Errors {
|
||||
|
||||
private static final Set<Dependency<?>> warnedDependencies =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<Dependency<?>, Boolean>());
|
||||
private static final String CONSTRUCTOR_RULES =
|
||||
"Classes must have either one (and only one) constructor "
|
||||
+ "annotated with @Inject or a zero-argument constructor that is not private.";
|
||||
private static final Collection<Converter<?>> converters = ImmutableList.of(
|
||||
new Converter<Class>(Class.class) {
|
||||
@Override
|
||||
public String toString(Class c) {
|
||||
return c.getName();
|
||||
}
|
||||
},
|
||||
new Converter<Member>(Member.class) {
|
||||
@Override
|
||||
public String toString(Member member) {
|
||||
return Classes.toString(member);
|
||||
}
|
||||
},
|
||||
new Converter<Key>(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();
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The root errors object. Used to access the list of error messages.
|
||||
*/
|
||||
/** When a binding is not found, show at most this many bindings with the same type */
|
||||
private static final int MAX_MATCHING_TYPES_REPORTED = 3;
|
||||
|
||||
/** When a binding is not found, show at most this many bindings that have some similarities */
|
||||
private static final int MAX_RELATED_TYPES_REPORTED = 3;
|
||||
|
||||
static <T> T checkNotNull(T reference, String name) {
|
||||
if (reference != null) {
|
||||
return reference;
|
||||
}
|
||||
NullPointerException npe = new NullPointerException(name);
|
||||
throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
|
||||
}
|
||||
|
||||
static void checkConfiguration(boolean condition, String format, Object... args) {
|
||||
if (condition) {
|
||||
return;
|
||||
}
|
||||
throw new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
|
||||
}
|
||||
|
||||
private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
|
||||
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;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The leaf source for errors added here.
|
||||
*/
|
||||
|
@ -120,246 +109,101 @@ public final class Errors {
|
|||
this.source = source;
|
||||
}
|
||||
|
||||
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 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.
|
||||
*/
|
||||
/** Returns an instance that uses {@code source} as a reference point for newly added errors. */
|
||||
public Errors withSource(Object source) {
|
||||
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
|
||||
? this
|
||||
: new Errors(this, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
public Errors missingImplementation(Key<?> 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);
|
||||
}
|
||||
|
||||
public Errors jitDisabledInParent(Key<?> key) {
|
||||
return addMessage(
|
||||
"Explicit bindings are required and %s would be bound in a parent injector.%n"
|
||||
return addMessage("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.",
|
||||
key);
|
||||
}
|
||||
|
||||
public Errors atInjectRequired(Class clazz) {
|
||||
return addMessage(
|
||||
"Explicit @Inject annotations are required on constructors,"
|
||||
public Errors atInjectRequired(Class<?> clazz) {
|
||||
return addMessage("Explicit @Inject annotations are required on constructors,"
|
||||
+ " but %s has no constructors annotated with @Inject.",
|
||||
clazz);
|
||||
}
|
||||
|
@ -368,7 +212,7 @@ public final class Errors {
|
|||
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
|
||||
return addMessage("Received null converting '%s' (bound at %s) to %s%n"
|
||||
+ " using %s.",
|
||||
stringValue, convert(source), type, typeConverterBinding);
|
||||
stringValue, Messages.convert(source), type, typeConverterBinding);
|
||||
}
|
||||
|
||||
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"
|
||||
+ " using %s.%n"
|
||||
+ " Converter returned %s.",
|
||||
stringValue, convert(source), type, typeConverterBinding, converted);
|
||||
stringValue, Messages.convert(source), type, typeConverterBinding, converted);
|
||||
}
|
||||
|
||||
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"
|
||||
+ " using %s.%n"
|
||||
+ " Reason: %s",
|
||||
stringValue, convert(source), type, typeConverterBinding, cause);
|
||||
stringValue, Messages.convert(source), type, typeConverterBinding, cause);
|
||||
}
|
||||
|
||||
public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
|
||||
|
@ -393,18 +237,13 @@ public final class Errors {
|
|||
+ " %s and%n"
|
||||
+ " %s.%n"
|
||||
+ " 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() {
|
||||
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) {
|
||||
return addMessage("%s doesn't extend %s.", implementationType, type);
|
||||
}
|
||||
|
@ -418,14 +257,14 @@ public final class Errors {
|
|||
}
|
||||
|
||||
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) {
|
||||
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), "
|
||||
+ "but constructors cannot be optional.", constructor);
|
||||
}
|
||||
|
@ -441,7 +280,7 @@ public final class Errors {
|
|||
public Errors scopeAnnotationOnAbstractType(
|
||||
Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
|
||||
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) {
|
||||
|
@ -449,14 +288,27 @@ public final class Errors {
|
|||
+ "to its parameters instead.", member, bindingAnnotation);
|
||||
}
|
||||
|
||||
public Errors missingConstructor(Class<?> implementation) {
|
||||
return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES,
|
||||
implementation);
|
||||
private static final String CONSTRUCTOR_RULES =
|
||||
"Injectable classes must have either one (and only one) constructor annotated with @Inject"
|
||||
+ " 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) {
|
||||
return addMessage("%s has more than one constructor annotated with @Inject. "
|
||||
+ CONSTRUCTOR_RULES, implementation);
|
||||
return addMessage(
|
||||
"%s has more than one constructor annotated with @Inject. %s",
|
||||
implementation, CONSTRUCTOR_RULES);
|
||||
}
|
||||
|
||||
public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
|
||||
|
@ -518,7 +370,7 @@ public final class Errors {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -534,24 +386,19 @@ public final class Errors {
|
|||
allSources.format("%n bound at %s", source);
|
||||
}
|
||||
}
|
||||
Errors errors = addMessage(
|
||||
return addMessage(
|
||||
"Unable to create binding for %s."
|
||||
+ " It was already configured on one or more child injectors or private modules"
|
||||
+ "%s%n"
|
||||
+ " If it was in a PrivateModule, did you forget to expose the binding?",
|
||||
key, allSources.out());
|
||||
return errors;
|
||||
}
|
||||
|
||||
public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
|
||||
return addMessage(
|
||||
"A binding to %s was already configured at %s and an error was thrown "
|
||||
+ "while checking duplicate bindings. Error: %s",
|
||||
key, convert(source), t);
|
||||
}
|
||||
|
||||
public Errors errorInjectingMethod(Throwable cause) {
|
||||
return errorInUserCode(cause, "Error injecting method, %s", cause);
|
||||
key, Messages.convert(source), t);
|
||||
}
|
||||
|
||||
public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
|
||||
|
@ -559,28 +406,7 @@ public final class Errors {
|
|||
return errorInUserCode(cause,
|
||||
"Error notifying TypeListener %s (bound at %s) of %s.%n"
|
||||
+ " Reason: %s",
|
||||
listener.getListener(), 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);
|
||||
listener.getListener(), Messages.convert(listener.getSource()), type, cause);
|
||||
}
|
||||
|
||||
public Errors exposedButNotBound(Key<?> key) {
|
||||
|
@ -595,6 +421,18 @@ public final class Errors {
|
|||
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) {
|
||||
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() {
|
||||
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");
|
||||
}
|
||||
|
||||
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() {
|
||||
if (!hasErrors()) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new CreationException(getMessages());
|
||||
}
|
||||
|
||||
|
@ -653,28 +471,13 @@ public final class Errors {
|
|||
if (!hasErrors()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
List<Object> sources = getSources();
|
||||
for (Message message : messages) {
|
||||
addMessage(merge(message));
|
||||
addMessage(Messages.mergeSources(sources, message));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -683,11 +486,15 @@ public final class Errors {
|
|||
if (moreErrors.root == root || moreErrors.root.errors == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
merge(moreErrors.root.errors);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Errors merge(InternalProvisionException ipe) {
|
||||
merge(ipe.getErrors());
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<Object> getSources() {
|
||||
List<Object> sources = Lists.newArrayList();
|
||||
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) {
|
||||
String message = format(messageFormat, arguments);
|
||||
addMessage(new Message(getSources(), message, cause));
|
||||
addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -736,7 +542,6 @@ public final class Errors {
|
|||
if (root.errors == null) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
return new Ordering<Message>() {
|
||||
@Override
|
||||
public int compare(Message a, Message b) {
|
||||
|
@ -745,73 +550,7 @@ public final class 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() {
|
||||
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")
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateElements getPrivateElements() {
|
||||
return privateElements;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
|||
this.privateElements = privateElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(Errors errors) {
|
||||
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
||||
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
|
||||
|
@ -33,8 +34,8 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
|||
this.delegate = explicitBinding;
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
return delegate.getInternalFactory().get(errors, context, dependency, linked);
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
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) {
|
||||
Preconditions.checkNotNull(annotationType, "annotationType");
|
||||
checkNotAnnotated();
|
||||
key = Key.get(key.getTypeLiteral(), annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void annotatedWith(Annotation annotation) {
|
||||
Preconditions.checkNotNull(annotation, "annotation");
|
||||
checkNotAnnotated();
|
||||
|
|
|
@ -25,6 +25,7 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
|||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(final Errors errors) {
|
||||
try {
|
||||
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)
|
||||
throws ErrorsException {
|
||||
context.pushState(targetKey, source);
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
Key<? extends T> localTargetKey = targetKey;
|
||||
context.pushState(localTargetKey, source);
|
||||
try {
|
||||
return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
|
||||
return targetFactory.get(context, dependency, true);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(localTargetKey);
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package com.google.inject.internal;
|
|||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
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
|
||||
|
@ -10,8 +13,8 @@ import com.google.common.cache.LoadingCache;
|
|||
*/
|
||||
public abstract class FailableCache<K, V> {
|
||||
|
||||
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<K, Object>() {
|
||||
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<>() {
|
||||
public Object load(K key) {
|
||||
Errors errors = new Errors();
|
||||
V result = null;
|
||||
|
@ -33,7 +36,7 @@ public abstract class FailableCache<K, V> {
|
|||
throw errors.toException();
|
||||
} else {
|
||||
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
|
||||
V result = (V) resultOrError;
|
||||
V result = (V) resultOrError;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -41,4 +44,14 @@ public abstract class FailableCache<K, V> {
|
|||
boolean remove(K key) {
|
||||
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.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
|
||||
* 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> {
|
||||
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;
|
||||
}
|
||||
|
||||
boolean isIndexable(Binding<?> binding) {
|
||||
public boolean isIndexable(Binding<?> binding) {
|
||||
return binding.getKey().getAnnotation() instanceof Element;
|
||||
}
|
||||
|
||||
|
@ -126,7 +131,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
|||
PROVIDED_BY,
|
||||
}
|
||||
|
||||
static class IndexedBinding {
|
||||
public static class IndexedBinding {
|
||||
final String annotationName;
|
||||
final Element.Type annotationType;
|
||||
final TypeLiteral<?> typeLiteral;
|
||||
|
@ -134,7 +139,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
|||
final BindingType type;
|
||||
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.type = type;
|
||||
this.extraEquality = extraEquality;
|
|
@ -3,17 +3,23 @@ package com.google.inject.internal;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Binding;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Scope;
|
||||
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.ProviderLookup;
|
||||
import com.google.inject.spi.ProvisionListenerBinding;
|
||||
import com.google.inject.spi.ScopeBinding;
|
||||
import com.google.inject.spi.StaticInjectionRequest;
|
||||
import com.google.inject.spi.TypeConverterBinding;
|
||||
import com.google.inject.spi.TypeListenerBinding;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.
|
||||
private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
|
||||
private final Map<Key<?>, Binding<?>> explicitBindings
|
||||
= Collections.unmodifiableMap(explicitBindingsMutable);
|
||||
private final Map<Key<?>, Binding<?>> explicitBindings =
|
||||
Collections.unmodifiableMap(explicitBindingsMutable);
|
||||
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<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
|
||||
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
|
||||
|
@ -43,41 +53,95 @@ final class InheritingState implements State {
|
|||
this.blacklistedKeys = new WeakKeySet(lock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State parent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
|
||||
@Override
|
||||
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
|
||||
Binding<?> binding = explicitBindings.get(key);
|
||||
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
|
||||
return explicitBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBinding(Key<?> key, BindingImpl<?> 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) {
|
||||
ScopeBinding scopeBinding = scopes.get(annotationType);
|
||||
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
|
||||
scopes.put(annotationType, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ScopeBinding> getScopeBindingsThisLevel() {
|
||||
return scopes.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
|
||||
return converters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addConverter(TypeConverterBinding typeConverterBinding) {
|
||||
converters.add(typeConverterBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeConverterBinding getConverter(
|
||||
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
|
||||
TypeConverterBinding matchingConverter = null;
|
||||
|
@ -94,10 +158,12 @@ final class InheritingState implements State {
|
|||
return matchingConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTypeListener(TypeListenerBinding listenerBinding) {
|
||||
typeListenerBindings.add(listenerBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeListenerBinding> getTypeListenerBindings() {
|
||||
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
|
||||
List<TypeListenerBinding> result =
|
||||
|
@ -107,10 +173,17 @@ final class InheritingState implements State {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeListenerBinding> getTypeListenerBindingsThisLevel() {
|
||||
return typeListenerBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
|
||||
provisionListenerBindings.add(listenerBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
|
||||
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
|
||||
List<ProvisionListenerBinding> result =
|
||||
|
@ -120,10 +193,17 @@ final class InheritingState implements State {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProvisionListenerBinding> getProvisionListenerBindingsThisLevel() {
|
||||
return provisionListenerBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
|
||||
scannerBindings.add(scanner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
|
||||
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
|
||||
List<ModuleAnnotatedMethodScannerBinding> result =
|
||||
|
@ -133,23 +213,33 @@ final class InheritingState implements State {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindingsThisLevel() {
|
||||
return scannerBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blacklist(Key<?> key, State state, Object source) {
|
||||
parent.blacklist(key, state, source);
|
||||
blacklistedKeys.add(key, state, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlacklisted(Key<?> key) {
|
||||
return blacklistedKeys.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
|
||||
return blacklistedKeys.getSources(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object lock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Class<? extends Annotation>, Scope> getScopes() {
|
||||
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static <T> Initializable<T> of(final T instance) {
|
||||
return new Initializable<T>() {
|
||||
public T get(Errors errors) throws ErrorsException {
|
||||
return new Initializable<>() {
|
||||
public T get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,16 @@ package com.google.inject.internal;
|
|||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Binding;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Stage;
|
||||
import com.google.inject.TypeLiteral;
|
||||
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.concurrent.CountDownLatch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
@ -23,27 +24,30 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
*/
|
||||
final class Initializer {
|
||||
|
||||
/**
|
||||
* the only thread that we'll use to inject members.
|
||||
*/
|
||||
private final Thread creatingThread = Thread.currentThread();
|
||||
/** Is set to true once {@link #validateOustandingInjections} is called. */
|
||||
private volatile boolean validationStarted = false;
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*
|
||||
|
@ -55,21 +59,35 @@ final class Initializer {
|
|||
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
|
||||
Object source, Set<InjectionPoint> injectionPoints) {
|
||||
checkNotNull(source);
|
||||
|
||||
Preconditions.checkState(
|
||||
!validationStarted, "Member injection could not be requested after validation is started");
|
||||
ProvisionListenerStackCallback<T> provisionCallback =
|
||||
binding == null ? null : injector.provisionListenerStore.get(binding);
|
||||
|
||||
// short circuit if the object has no injections or listeners.
|
||||
if (instance == null || (injectionPoints.isEmpty()
|
||||
&& !injector.membersInjectorStore.hasTypeListeners()
|
||||
&& (provisionCallback == null || !provisionCallback.hasListeners()))) {
|
||||
&& (provisionCallback == null))) {
|
||||
return Initializables.of(instance);
|
||||
}
|
||||
|
||||
InjectableReference<T> initializable = new InjectableReference<T>(
|
||||
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
|
||||
pendingInjection.put(instance, initializable);
|
||||
return initializable;
|
||||
if (initializablesCache.containsKey(instance)) {
|
||||
@SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
|
||||
Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
|
||||
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.
|
||||
*/
|
||||
void validateOustandingInjections(Errors errors) {
|
||||
for (InjectableReference<?> reference : pendingInjection.values()) {
|
||||
validationStarted = true;
|
||||
initializablesCache.clear();
|
||||
for (InjectableReference<?> reference : pendingInjections) {
|
||||
try {
|
||||
pendingMembersInjectors.put(reference.instance, reference.validate(errors));
|
||||
reference.validate(errors);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
|
@ -92,85 +112,122 @@ final class Initializer {
|
|||
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
||||
*/
|
||||
void injectAll(final Errors errors) {
|
||||
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
|
||||
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
|
||||
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
|
||||
Preconditions.checkState(validationStarted, "Validation should be done before injection");
|
||||
for (InjectableReference<?> reference : pendingInjections) {
|
||||
try {
|
||||
reference.get(errors);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
reference.get();
|
||||
} catch (InternalProvisionException ipe) {
|
||||
errors.merge(ipe);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pendingInjection.isEmpty()) {
|
||||
throw new AssertionError("Failed to satisfy " + pendingInjection);
|
||||
}
|
||||
|
||||
ready.countDown();
|
||||
pendingInjections.clear();
|
||||
}
|
||||
|
||||
private class InjectableReference<T> implements Initializable<T> {
|
||||
private enum InjectableReferenceState {
|
||||
NEW,
|
||||
VALIDATED,
|
||||
INJECTING,
|
||||
READY
|
||||
}
|
||||
|
||||
private static class InjectableReference<T> implements Initializable<T> {
|
||||
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
|
||||
private volatile MembersInjectorImpl<T> membersInjector = null;
|
||||
|
||||
private final InjectorImpl injector;
|
||||
private final T instance;
|
||||
private final Object source;
|
||||
private final Key<T> key;
|
||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||
private final CycleDetectingLock<?> lock;
|
||||
|
||||
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.key = key; // possibly null!
|
||||
this.provisionCallback = provisionCallback; // possibly null!
|
||||
this.instance = checkNotNull(instance, "instance");
|
||||
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>
|
||||
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||
return injector.membersInjectorStore.get(type, errors.withSource(source));
|
||||
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||
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
|
||||
* method will ensure that all its members have been injected before returning.
|
||||
*/
|
||||
public T get(Errors errors) throws ErrorsException {
|
||||
if (ready.getCount() == 0) {
|
||||
@Override
|
||||
public T get() throws InternalProvisionException {
|
||||
// skipping acquiring lock if initialization is already finished
|
||||
if (state == InjectableReferenceState.READY) {
|
||||
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
|
||||
if (Thread.currentThread() != creatingThread) {
|
||||
try {
|
||||
ready.await();
|
||||
return instance;
|
||||
} catch (InterruptedException e) {
|
||||
// Give up, since we don't know if our injection is ready
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// This condition should not be possible under the current Guice implementation.
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
try {
|
||||
// lock acquired, current thread owns this instance initialization
|
||||
switch (state) {
|
||||
case READY:
|
||||
return instance;
|
||||
// When instance depends on itself in the same thread potential dead lock
|
||||
// is not detected. We have to prevent a stack overflow and we use
|
||||
// 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);
|
||||
}
|
||||
|
||||
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
||||
// (otherwise we'll inject all of them)
|
||||
membersInjector.injectAndNotify(instance,
|
||||
errors.withSource(source),
|
||||
key,
|
||||
provisionCallback,
|
||||
source,
|
||||
injector.options.stage == Stage.TOOL);
|
||||
try {
|
||||
membersInjector.injectAndNotify(
|
||||
instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(source);
|
||||
}
|
||||
// mark instance as ready to skip a lock on subsequent calls
|
||||
state = InjectableReferenceState.READY;
|
||||
return instance;
|
||||
} finally {
|
||||
// always release our creation lock, even on failures
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -98,21 +98,19 @@ final class InjectionRequestProcessor extends AbstractProcessor {
|
|||
}
|
||||
|
||||
void injectMembers() {
|
||||
try {
|
||||
injector.callInContext(new ContextualCallable<Void>() {
|
||||
public Void call(InternalContext context) {
|
||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||
// 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.
|
||||
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
|
||||
memberInjector.inject(errors, context, null);
|
||||
}
|
||||
try (InternalContext context = injector.enterContext()) {
|
||||
boolean isStageTool = injector.options.stage == Stage.TOOL;
|
||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||
// 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.
|
||||
if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
|
||||
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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
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.Multimaps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Binding;
|
||||
|
@ -18,7 +22,6 @@ import com.google.inject.MembersInjector;
|
|||
import com.google.inject.Module;
|
||||
import com.google.inject.ProvidedBy;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.Scope;
|
||||
import com.google.inject.Stage;
|
||||
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.ConvertedConstantBinding;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.Element;
|
||||
import com.google.inject.spi.HasDependencies;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.InstanceBinding;
|
||||
import com.google.inject.spi.ProviderBinding;
|
||||
import com.google.inject.spi.TypeConverterBinding;
|
||||
import com.google.inject.util.Providers;
|
||||
|
@ -42,45 +47,46 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Default {@link Injector} implementation.
|
||||
*
|
||||
*/
|
||||
final class InjectorImpl implements Injector, Lookups {
|
||||
|
||||
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 InjectorImpl parent;
|
||||
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
|
||||
|
||||
final ListMultimap<TypeLiteral<?>, Binding<?>> bindingsMultimap = ArrayListMultimap.create();
|
||||
|
||||
final InjectorOptions options;
|
||||
|
||||
/**
|
||||
* Just-in-time binding cache. Guarded by state.lock()
|
||||
*/
|
||||
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* Cache of Keys that we were unable to create JIT bindings for, so we don't
|
||||
* keep trying. Also guarded by state.lock().
|
||||
*/
|
||||
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
|
||||
*/
|
||||
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
|
||||
/**
|
||||
* @see #getGlobalInternalContext
|
||||
*/
|
||||
|
||||
private final ThreadLocal<Object[]> localContext;
|
||||
Lookups lookups = new DeferredLookups(this);
|
||||
|
||||
/**
|
||||
* Cached field and method injectors for each type.
|
||||
*/
|
||||
|
@ -98,56 +104,32 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
if (parent != null) {
|
||||
localContext = parent.localContext;
|
||||
} 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).
|
||||
*/
|
||||
private static boolean isProvider(Key<?> key) {
|
||||
return key.getTypeLiteral().getRawType().equals(Provider.class);
|
||||
|
||||
/** Only to be called by the {@link SingletonScope} provider. */
|
||||
InternalContext getLocalContext() {
|
||||
return (InternalContext) localContext.get()[0];
|
||||
}
|
||||
|
||||
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();
|
||||
InternalContext enterContext() {
|
||||
Object[] reference = localContext.get();
|
||||
if (reference == null) {
|
||||
reference = new Object[1];
|
||||
localContext.set(reference);
|
||||
}
|
||||
|
||||
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);
|
||||
InternalContext ctx = (InternalContext) reference[0];
|
||||
if (ctx == null) {
|
||||
reference[0] = ctx = new InternalContext(options, reference);
|
||||
} else {
|
||||
ctx.enter();
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,23 +137,23 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
*/
|
||||
void index() {
|
||||
for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) {
|
||||
index(binding);
|
||||
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
|
||||
}
|
||||
}
|
||||
|
||||
<T> void index(Binding<T> binding) {
|
||||
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
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}
|
||||
*/
|
||||
@Override
|
||||
public <T> BindingImpl<T> getBinding(Key<T> key) {
|
||||
Errors errors = new Errors(key);
|
||||
Errors errors = new Errors(checkNotNull(key, "key"));
|
||||
try {
|
||||
BindingImpl<T> result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT);
|
||||
errors.throwConfigurationExceptionIfErrorsExist();
|
||||
|
@ -181,6 +163,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> BindingImpl<T> getExistingBinding(Key<T> key) {
|
||||
// Check explicit bindings, i.e. bindings created by modules.
|
||||
BindingImpl<T> explicitBinding = state.getExplicitBinding(key);
|
||||
|
@ -235,21 +218,22 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
return getJustInTimeBinding(key, errors, jitType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Binding<T> getBinding(Class<T> type) {
|
||||
return getBinding(Key.get(type));
|
||||
return getBinding(Key.get(checkNotNull(type, "type")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
||||
return new InternalInjectorCreator()
|
||||
.parentInjector(this)
|
||||
.addModules(modules)
|
||||
.build();
|
||||
return new InternalInjectorCreator().parentInjector(this).addModules(modules).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector createChildInjector(Module... modules) {
|
||||
return createChildInjector(ImmutableList.copyOf(modules));
|
||||
}
|
||||
|
@ -267,7 +251,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
// first try to find a JIT binding that we've already created
|
||||
for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
|
||||
@SuppressWarnings("unchecked") // we only store bindings that match their key
|
||||
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
|
||||
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
|
||||
|
||||
if (binding != null) {
|
||||
// If we found a JIT binding and we don't allow them,
|
||||
|
@ -304,6 +288,40 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
} // 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(
|
||||
Key<MembersInjector<T>> key, Errors errors) throws ErrorsException {
|
||||
Type membersInjectorType = key.getTypeLiteral().getType();
|
||||
|
@ -312,16 +330,15 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // safe because T came from Key<MembersInjector<T>>
|
||||
TypeLiteral<T> instanceType = (TypeLiteral<T>) TypeLiteral.get(
|
||||
TypeLiteral<T> instanceType = (TypeLiteral<T>) TypeLiteral.get(
|
||||
((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]);
|
||||
MembersInjector<T> membersInjector = membersInjectorStore.get(instanceType, errors);
|
||||
|
||||
InternalFactory<MembersInjector<T>> factory = new ConstantFactory<MembersInjector<T>>(
|
||||
Initializables.of(membersInjector));
|
||||
InternalFactory<MembersInjector<T>> factory =
|
||||
new ConstantFactory<>(Initializables.of(membersInjector));
|
||||
|
||||
|
||||
return new InstanceBindingImpl<MembersInjector<T>>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
||||
factory, ImmutableSet.<InjectionPoint>of(), membersInjector);
|
||||
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
||||
factory, ImmutableSet.of(), membersInjector);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -350,7 +367,11 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
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();
|
||||
|
||||
// Find a matching type converter.
|
||||
|
@ -365,7 +386,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
// Try to convert the string. A failed conversion results in an error.
|
||||
try {
|
||||
@SuppressWarnings("unchecked") // This cast is safe because we double check below.
|
||||
T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type);
|
||||
T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type);
|
||||
|
||||
if (converted == null) {
|
||||
throw errors.converterReturnedNull(stringValue, source, type, typeConverterBinding)
|
||||
|
@ -379,8 +400,6 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
|
||||
return new ConvertedConstantBindingImpl<T>(this, key, converted, stringBinding,
|
||||
typeConverterBinding);
|
||||
} catch (ErrorsException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
throw errors.conversionError(stringValue, source, type, typeConverterBinding, e)
|
||||
.toException();
|
||||
|
@ -411,7 +430,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
// so that cached exceptions while constructing it get stored.
|
||||
// See TypeListenerTest#testTypeListenerThrows
|
||||
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
|
||||
* should have failed.
|
||||
*/
|
||||
private boolean cleanup(BindingImpl<?> binding, Set<Key> encountered) {
|
||||
private boolean cleanup(BindingImpl<?> binding, Set<Key<?>> encountered) {
|
||||
boolean bindingFailed = false;
|
||||
Set<Dependency<?>> deps = getInternalDependencies(binding);
|
||||
for (Dependency dep : deps) {
|
||||
for (Dependency<?> dep : deps) {
|
||||
Key<?> depKey = dep.getKey();
|
||||
InjectionPoint ip = dep.getInjectionPoint();
|
||||
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
|
||||
boolean failed = cleanup(depBinding, encountered); // if children fail, we fail
|
||||
if (depBinding instanceof ConstructorBindingImpl) {
|
||||
ConstructorBindingImpl ctorBinding = (ConstructorBindingImpl) depBinding;
|
||||
ConstructorBindingImpl<?> ctorBinding = (ConstructorBindingImpl) depBinding;
|
||||
ip = ctorBinding.getInternalConstructor();
|
||||
if (!ctorBinding.isInitialized()) {
|
||||
failed = true;
|
||||
|
@ -493,13 +512,13 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
|
||||
// Don't try to inject arrays or enums annotated with @ImplementedBy.
|
||||
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
|
||||
if (rawType == TypeLiteral.class) {
|
||||
@SuppressWarnings("unchecked") // we have to fudge the inner type as Object
|
||||
BindingImpl<T> binding = (BindingImpl<T>) createTypeLiteralBinding(
|
||||
BindingImpl<T> binding = (BindingImpl<T>) createTypeLiteralBinding(
|
||||
(Key<TypeLiteral<Object>>) key, errors);
|
||||
return binding;
|
||||
}
|
||||
|
@ -551,11 +570,10 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe
|
||||
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
|
||||
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<TypeLiteral<T>>(
|
||||
Initializables.of(value));
|
||||
return new InstanceBindingImpl<TypeLiteral<T>>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
||||
factory, ImmutableSet.<InjectionPoint>of(), value);
|
||||
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
|
||||
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<>(Initializables.of(value));
|
||||
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
|
||||
factory, ImmutableSet.of(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -564,7 +582,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
<T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
|
||||
ProvidedBy providedBy, Errors errors) throws ErrorsException {
|
||||
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?
|
||||
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);
|
||||
ProvidedByInternalFactory<T> internalFactory =
|
||||
new ProvidedByInternalFactory<T>(rawType, providerType, providerKey);
|
||||
Object source = rawType;
|
||||
BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer(
|
||||
this,
|
||||
key,
|
||||
source,
|
||||
Scoping.<T>scope(key, this, internalFactory, source, scoping),
|
||||
rawType,
|
||||
Scoping.<T>scope(key, this, internalFactory, rawType, scoping),
|
||||
scoping,
|
||||
providerKey,
|
||||
internalFactory);
|
||||
|
@ -613,27 +630,14 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
|
||||
// Look up the target binding.
|
||||
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>() {
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
context.pushState(targetKey, targetBinding.getSource());
|
||||
try {
|
||||
return targetBinding.getInternalFactory().get(
|
||||
errors.withSource(targetKey), context, dependency, true);
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object source = rawType;
|
||||
FactoryProxy<T> factory = new FactoryProxy<>(this, key, targetKey, rawType);
|
||||
factory.notify(errors); // causes the factory to initialize itself internally
|
||||
return new LinkedBindingImpl<T>(
|
||||
this,
|
||||
key,
|
||||
source,
|
||||
Scoping.<T>scope(key, this, internalFactory, source, scoping),
|
||||
rawType,
|
||||
Scoping.<T>scope(key, this, factory, rawType, scoping),
|
||||
scoping,
|
||||
targetKey);
|
||||
}
|
||||
|
@ -725,9 +729,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
return convertedBinding;
|
||||
}
|
||||
|
||||
if (!isTypeLiteral(key)
|
||||
&& jitDisabled
|
||||
&& jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
|
||||
if (!isTypeLiteral(key) && jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
|
||||
throw errors.jitDisabled(key).toException();
|
||||
}
|
||||
|
||||
|
@ -757,10 +759,12 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
return getBindingOrThrow(key, errors, jitType).getInternalFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key<?>, Binding<?>> getBindings() {
|
||||
return state.getExplicitBindingsThisLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key<?>, Binding<?>> getAllBindings() {
|
||||
synchronized (state.lock()) {
|
||||
return new ImmutableMap.Builder<Key<?>, Binding<?>>()
|
||||
|
@ -770,14 +774,47 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
||||
return ImmutableMap.copyOf(state.getScopes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypeConverterBinding> getTypeConverterBindings() {
|
||||
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.
|
||||
*/
|
||||
|
@ -808,13 +845,18 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
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) {
|
||||
MembersInjector membersInjector = getMembersInjector(instance.getClass());
|
||||
membersInjector.injectMembers(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||
checkNotNull(typeLiteral, "typeLiteral");
|
||||
userRequestedMembersInjectorTypes.add(typeLiteral);
|
||||
|
||||
Errors errors = new Errors(typeLiteral);
|
||||
try {
|
||||
return membersInjectorStore.get(typeLiteral, errors);
|
||||
|
@ -823,47 +865,45 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||
return getMembersInjector(TypeLiteral.get(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
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 {
|
||||
final Key<T> key = dependency.getKey();
|
||||
final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
|
||||
|
||||
return new Provider<T>() {
|
||||
final InternalFactory<? extends T> internalFactory = binding.getInternalFactory();
|
||||
final Object source = binding.getSource();
|
||||
return new Provider<>() {
|
||||
public T get() {
|
||||
final Errors errors = new Errors(dependency);
|
||||
InternalContext currentContext = enterContext();
|
||||
Dependency<?> previous = currentContext.pushDependency(dependency, source);
|
||||
try {
|
||||
T t = callInContext(new ContextualCallable<T>() {
|
||||
public T call(InternalContext context) throws ErrorsException {
|
||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
||||
try {
|
||||
return binding.getInternalFactory().get(errors, context, dependency, false);
|
||||
} finally {
|
||||
context.popStateAndSetDependency(previous);
|
||||
}
|
||||
}
|
||||
});
|
||||
errors.throwIfNewErrors(0);
|
||||
return t;
|
||||
} catch (ErrorsException e) {
|
||||
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
|
||||
return internalFactory.get(currentContext, dependency, false);
|
||||
} catch (InternalProvisionException e) {
|
||||
throw e.addSource(dependency).toProvisionException();
|
||||
} finally {
|
||||
currentContext.popStateAndSetDependency(previous);
|
||||
currentContext.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return binding.getInternalFactory().toString();
|
||||
return internalFactory.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Provider<T> getProvider(final Key<T> key) {
|
||||
checkNotNull(key, "key");
|
||||
Errors errors = new Errors(key);
|
||||
try {
|
||||
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) {
|
||||
return getProvider(key).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInstance(Class<T> type) {
|
||||
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
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(Injector.class)
|
||||
|
@ -994,21 +1000,20 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
|
||||
static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) {
|
||||
final Provider<T> provider = providedBinding.getProvider();
|
||||
return new InternalFactory<Provider<T>>() {
|
||||
public Provider<T> get(Errors errors, InternalContext context, Dependency dependency, boolean linked) {
|
||||
return provider;
|
||||
}
|
||||
};
|
||||
return (context, dependency, linked) -> provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<? extends T> getProvidedKey() {
|
||||
return providedBinding.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(Binder binder) {
|
||||
throw new UnsupportedOperationException("This element represents a synthetic binding.");
|
||||
}
|
||||
|
@ -1021,6 +1026,7 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey()));
|
||||
}
|
||||
|
@ -1050,9 +1056,11 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
final Binding<String> originalBinding;
|
||||
final TypeConverterBinding typeConverterBinding;
|
||||
|
||||
ConvertedConstantBindingImpl(
|
||||
InjectorImpl injector, Key<T> key, T value, Binding<String> originalBinding,
|
||||
TypeConverterBinding typeConverterBinding) {
|
||||
ConvertedConstantBindingImpl(InjectorImpl injector,
|
||||
Key<T> key,
|
||||
T value,
|
||||
Binding<String> originalBinding,
|
||||
TypeConverterBinding typeConverterBinding) {
|
||||
super(injector, key, originalBinding.getSource(),
|
||||
new ConstantFactory<T>(Initializables.of(value)), Scoping.UNSCOPED);
|
||||
this.value = value;
|
||||
|
@ -1066,26 +1074,32 @@ final class InjectorImpl implements Injector, Lookups {
|
|||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeConverterBinding getTypeConverterBinding() {
|
||||
return typeConverterBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<String> getSourceKey() {
|
||||
return originalBinding.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(Binder binder) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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.Element;
|
||||
import com.google.inject.spi.Elements;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||
import com.google.inject.spi.PrivateElements;
|
||||
import com.google.inject.spi.ProvisionListenerBinding;
|
||||
|
@ -35,11 +34,20 @@ final class InjectorShell {
|
|||
private final List<Element> elements;
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
|
||||
private InjectorShell(List<Element> elements, InjectorImpl injector) {
|
||||
this.elements = elements;
|
||||
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
|
||||
* a binding for that key.
|
||||
|
@ -48,44 +56,23 @@ final class InjectorShell {
|
|||
Key<Injector> key = Key.get(Injector.class);
|
||||
InjectorFactory injectorFactory = new InjectorFactory(injector);
|
||||
injector.state.putBinding(key,
|
||||
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
|
||||
new ProviderInstanceBindingImpl<>(injector, key, SourceProvider.UNKNOWN_SOURCE,
|
||||
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) {
|
||||
Key<Stage> key = Key.get(Stage.class);
|
||||
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
|
||||
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<>(
|
||||
injector,
|
||||
key,
|
||||
SourceProvider.UNKNOWN_SOURCE,
|
||||
new ConstantFactory<Stage>(Initializables.of(stage)),
|
||||
ImmutableSet.<InjectionPoint>of(),
|
||||
new ConstantFactory<>(Initializables.of(stage)),
|
||||
ImmutableSet.of(),
|
||||
stage);
|
||||
injector.state.putBinding(key, stageBinding);
|
||||
}
|
||||
|
||||
InjectorImpl getInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
List<Element> getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
private final List<Element> elements = Lists.newArrayList();
|
||||
private final List<Module> modules = Lists.newArrayList();
|
||||
|
@ -213,7 +200,7 @@ final class InjectorShell {
|
|||
stopwatch.resetAndLog("Module annotated method scanners creation");
|
||||
|
||||
List<InjectorShell> injectorShells = Lists.newArrayList();
|
||||
injectorShells.add(new InjectorShell(this, elements, injector));
|
||||
injectorShells.add(new InjectorShell(elements, injector));
|
||||
|
||||
// recursively build child shells
|
||||
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
||||
|
@ -241,11 +228,12 @@ final class InjectorShell {
|
|||
this.injector = injector;
|
||||
}
|
||||
|
||||
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
@Override
|
||||
public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
|
||||
return injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector get() {
|
||||
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 {
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
||||
binder.bindScope(Singleton.class, SINGLETON);
|
||||
|
@ -287,6 +259,7 @@ final class InjectorShell {
|
|||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
|
||||
binding.applyTo(binder);
|
||||
|
|
|
@ -5,20 +5,17 @@ import com.google.common.base.Objects;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.spi.BindingTargetVisitor;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.HasDependencies;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.InstanceBinding;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
||||
|
||||
final T instance;
|
||||
final Provider<T> provider;
|
||||
final ImmutableSet<InjectionPoint> injectionPoints;
|
||||
|
||||
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);
|
||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||
this.instance = instance;
|
||||
this.provider = Providers.of(instance);
|
||||
}
|
||||
|
||||
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);
|
||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||
this.instance = instance;
|
||||
this.provider = Providers.of(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<T> getProvider() {
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<InjectionPoint> getInjectionPoints() {
|
||||
return injectionPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return instance instanceof HasDependencies
|
||||
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
||||
: Dependency.forInjectionPoints(injectionPoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingImpl<T> withKey(Key<T> key) {
|
||||
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(Binder binder) {
|
||||
// instance bindings aren't scoped
|
||||
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
||||
|
|
|
@ -1,37 +1,76 @@
|
|||
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.internal.InjectorImpl.InjectorOptions;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.DependencyAndSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Internal context. Used to coordinate injections and support circular
|
||||
* dependencies.
|
||||
*/
|
||||
final class InternalContext {
|
||||
final class InternalContext implements AutoCloseable {
|
||||
|
||||
private final InjectorOptions options;
|
||||
/**
|
||||
* Keeps track of the hierarchy of types needed during injection.
|
||||
*/
|
||||
private final DependencyStack state = new DependencyStack();
|
||||
private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
|
||||
|
||||
private final Map<Object, ConstructionContext<?>> constructionContexts =
|
||||
new IdentityHashMap<>();
|
||||
|
||||
/**
|
||||
* Keeps track of the type that is currently being requested for injection.
|
||||
*/
|
||||
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.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() {
|
||||
return options;
|
||||
}
|
||||
|
@ -57,7 +96,7 @@ final class InternalContext {
|
|||
public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
|
||||
Dependency<?> previous = this.dependency;
|
||||
this.dependency = dependency;
|
||||
state.add(dependency, source);
|
||||
doPushState(dependency, source);
|
||||
return previous;
|
||||
}
|
||||
|
||||
|
@ -65,7 +104,7 @@ final class InternalContext {
|
|||
* Pops the current state & sets the new dependency.
|
||||
*/
|
||||
public void popStateAndSetDependency(Dependency<?> newDependency) {
|
||||
state.pop();
|
||||
popState();
|
||||
this.dependency = newDependency;
|
||||
}
|
||||
|
||||
|
@ -73,64 +112,29 @@ final class InternalContext {
|
|||
* Adds to the state without setting the dependency.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public void popState() {
|
||||
state.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current dependency chain (all the state).
|
||||
*/
|
||||
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;
|
||||
}
|
||||
// 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.
|
||||
dependencyStackSize -= 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,9 @@ interface InternalFactory<T> {
|
|||
*
|
||||
* @param context of this injection
|
||||
* @param linked true if getting as a result of a linked binding
|
||||
* @return instance to be injected
|
||||
* @throws com.google.inject.internal.ErrorsException if a value cannot be provided
|
||||
* @return instance that was created
|
||||
* @throws InternalProvisionException if a value cannot be provided
|
||||
*/
|
||||
T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException;
|
||||
T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
|
|||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||
private final Initializable<? extends javax.inject.Provider<? extends T>> initializable;
|
||||
|
||||
public InternalFactoryToInitializableAdapter(
|
||||
InternalFactoryToInitializableAdapter(
|
||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable,
|
||||
Object source, ProvisionListenerStackCallback<T> provisionCallback) {
|
||||
super(source);
|
||||
|
@ -22,19 +22,20 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
|
|||
this.initializable = checkNotNull(initializable, "provider");
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
return circularGet(initializable.get(errors), errors, context, dependency,
|
||||
provisionCallback);
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
return circularGet(initializable.get(), context, dependency, provisionCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
|
||||
Dependency<?> dependency, ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||
protected T provision(javax.inject.Provider<? extends T> provider,
|
||||
Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||
try {
|
||||
return super.provision(provider, errors, dependency, constructionContext);
|
||||
return super.provision(provider, dependency, constructionContext);
|
||||
} 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 Object source;
|
||||
|
||||
public InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
|
||||
InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
|
||||
this.provider = checkNotNull(provider, "provider");
|
||||
this.source = checkNotNull(source, "source");
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
// TODO(sameb): Does this need to push state into the context?
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
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) {
|
||||
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,
|
||||
T secureValue) {
|
||||
Class<T> enumType = defaultValue.getDeclaringClass();
|
||||
String value = null;
|
||||
try {
|
||||
value = AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
public String run() {
|
||||
return System.getProperty(name);
|
||||
}
|
||||
});
|
||||
String value = AccessController.doPrivileged((PrivilegedAction<String>) ()
|
||||
-> System.getProperty(name));
|
||||
return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue;
|
||||
} catch (SecurityException e) {
|
||||
return secureValue;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
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.Injector;
|
||||
import com.google.inject.Key;
|
||||
|
@ -13,9 +11,12 @@ import com.google.inject.Stage;
|
|||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.internal.util.Stopwatch;
|
||||
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 java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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
|
||||
* stage to the stage of {@code parent}.
|
||||
*/
|
||||
public InternalInjectorCreator parentInjector(InjectorImpl parent) {
|
||||
InternalInjectorCreator parentInjector(InjectorImpl parent) {
|
||||
shellBuilder.parent(parent);
|
||||
return this;
|
||||
}
|
||||
|
@ -102,36 +103,32 @@ public final class InternalInjectorCreator {
|
|||
private void initializeStatically() {
|
||||
bindingData.initializeBindings();
|
||||
stopwatch.resetAndLog("Binding initialization");
|
||||
|
||||
for (InjectorShell shell : shells) {
|
||||
shell.getInjector().index();
|
||||
}
|
||||
stopwatch.resetAndLog("Binding indexing");
|
||||
|
||||
injectionRequestProcessor.process(shells);
|
||||
stopwatch.resetAndLog("Collecting injection requests");
|
||||
|
||||
bindingData.runCreationListeners(errors);
|
||||
stopwatch.resetAndLog("Binding validation");
|
||||
|
||||
injectionRequestProcessor.validate();
|
||||
stopwatch.resetAndLog("Static validation");
|
||||
|
||||
initializer.validateOustandingInjections(errors);
|
||||
stopwatch.resetAndLog("Instance member validation");
|
||||
|
||||
new LookupProcessor(errors).process(shells);
|
||||
for (InjectorShell shell : shells) {
|
||||
((DeferredLookups) shell.getInjector().lookups).initialize(errors);
|
||||
}
|
||||
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) {
|
||||
if (!shell.getElements().isEmpty()) {
|
||||
throw new AssertionError("Failed to execute " + shell.getElements());
|
||||
}
|
||||
}
|
||||
|
||||
errors.throwCreationExceptionIfErrorsExist();
|
||||
}
|
||||
|
||||
|
@ -170,31 +167,25 @@ public final class InternalInjectorCreator {
|
|||
*/
|
||||
void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
|
||||
@SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
|
||||
Iterable<BindingImpl<?>> candidateBindings = ImmutableList.copyOf(Iterables.concat(
|
||||
(Collection) injector.state.getExplicitBindingsThisLevel().values(),
|
||||
injector.jitBindings.values()));
|
||||
for (final BindingImpl<?> binding : candidateBindings) {
|
||||
if (isEagerSingleton(injector, binding, stage)) {
|
||||
try {
|
||||
injector.callInContext(new ContextualCallable<Void>() {
|
||||
Dependency<?> dependency = Dependency.get(binding.getKey());
|
||||
|
||||
public Void call(InternalContext context) {
|
||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
||||
Errors errorsForBinding = errors.withSource(dependency);
|
||||
try {
|
||||
binding.getInternalFactory().get(errorsForBinding, context, dependency, false);
|
||||
} catch (ErrorsException e) {
|
||||
errorsForBinding.merge(e.getErrors());
|
||||
} finally {
|
||||
context.popStateAndSetDependency(previous);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (ErrorsException e) {
|
||||
throw new AssertionError();
|
||||
Collection<BindingImpl<?>> bindingsAtThisLevel =
|
||||
(Collection) injector.state.getExplicitBindingsThisLevel().values();
|
||||
List<BindingImpl<?>> candidateBindings = new ArrayList<>(bindingsAtThisLevel);
|
||||
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)) {
|
||||
Dependency<?> dependency = Dependency.get(binding.getKey());
|
||||
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
|
||||
try {
|
||||
binding.getInternalFactory().get(context, dependency, false);
|
||||
} catch (InternalProvisionException e) {
|
||||
errors.withSource(dependency).merge(e);
|
||||
} finally {
|
||||
context.popStateAndSetDependency(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
// 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) {
|
||||
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
|
||||
return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
|
||||
return binding.getScoping().isNoScope() &&
|
||||
isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -226,80 +218,108 @@ public final class InternalInjectorCreator {
|
|||
this.delegateInjector = delegateInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMembers(Object o) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Injector.injectMembers(Object) is not supported in Stage.TOOL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key<?>, Binding<?>> getBindings() {
|
||||
return this.delegateInjector.getBindings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key<?>, Binding<?>> getAllBindings() {
|
||||
return this.delegateInjector.getAllBindings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Binding<T> getBinding(Key<T> key) {
|
||||
return this.delegateInjector.getBinding(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Binding<T> getBinding(Class<T> type) {
|
||||
return this.delegateInjector.getBinding(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Binding<T> getExistingBinding(Key<T> key) {
|
||||
return this.delegateInjector.getExistingBinding(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
|
||||
return this.delegateInjector.findBindingsByType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector getParent() {
|
||||
return delegateInjector.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
||||
return delegateInjector.createChildInjector(modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector createChildInjector(Module... modules) {
|
||||
return delegateInjector.createChildInjector(modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
||||
return delegateInjector.getScopeBindings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypeConverterBinding> 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) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Provider<T> getProvider(Class<T> type) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInstance(Key<T> key) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInstance(Class<T> type) {
|
||||
throw new UnsupportedOperationException(
|
||||
"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>
|
||||
extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize {
|
||||
|
||||
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||
final DelayedInitialize delayedInitializer;
|
||||
private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||
|
||||
private final DelayedInitialize delayedInitializer;
|
||||
|
||||
private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||
|
@ -27,7 +28,7 @@ final class LinkedProviderBindingImpl<T>
|
|||
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,
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||
this(injector, key, source, internalFactory, scoping, providerKey, null);
|
||||
|
@ -44,36 +45,43 @@ final class LinkedProviderBindingImpl<T>
|
|||
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey,
|
||||
DelayedInitialize delayedInitializer) {
|
||||
return new LinkedProviderBindingImpl<T>(injector, key, source, internalFactory, scoping,
|
||||
return new LinkedProviderBindingImpl<>(injector, key, source, internalFactory, scoping,
|
||||
providerKey, delayedInitializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
|
||||
return providerKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||
if (delayedInitializer != null) {
|
||||
delayedInitializer.initialize(injector, errors);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
|
||||
return ImmutableSet.of(Dependency.get(providerKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey);
|
||||
return new LinkedProviderBindingImpl<>(getSource(), key, getScoping(), providerKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(Binder binder) {
|
||||
getScoping().applyTo(binder.withSource(getSource())
|
||||
.bind(getKey()).toProvider(getProviderKey()));
|
||||
|
|
|
@ -13,68 +13,68 @@ import com.google.inject.spi.InjectionPoint;
|
|||
* Injects members of instances of a given type.
|
||||
*/
|
||||
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,
|
||||
EncounterImpl<T> encounter, ImmutableList<SingleMemberInjector> memberInjectors) {
|
||||
private final TypeLiteral<T> typeLiteral;
|
||||
|
||||
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.typeLiteral = typeLiteral;
|
||||
this.memberInjectors = memberInjectors;
|
||||
this.userMembersInjectors = encounter.getMembersInjectors();
|
||||
this.injectionListeners = encounter.getInjectionListeners();
|
||||
this.memberInjectors = memberInjectors.isEmpty() ? null : memberInjectors;
|
||||
this.userMembersInjectors =
|
||||
encounter.getMembersInjectors().isEmpty() ? null : encounter.getMembersInjectors().asList();
|
||||
this.injectionListeners =
|
||||
encounter.getInjectionListeners().isEmpty()
|
||||
? null
|
||||
: encounter.getInjectionListeners().asList();
|
||||
}
|
||||
|
||||
public ImmutableList<SingleMemberInjector> getMemberInjectors() {
|
||||
return memberInjectors;
|
||||
return memberInjectors == null ? ImmutableList.of() : memberInjectors;
|
||||
}
|
||||
|
||||
public void injectMembers(T instance) {
|
||||
Errors errors = new Errors(typeLiteral);
|
||||
TypeLiteral<T> localTypeLiteral = typeLiteral;
|
||||
try {
|
||||
injectAndNotify(instance, errors, null, null, typeLiteral, false);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
injectAndNotify(instance, null, null, localTypeLiteral, false);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(localTypeLiteral).toProvisionException();
|
||||
}
|
||||
|
||||
errors.throwProvisionExceptionIfErrorsExist();
|
||||
}
|
||||
|
||||
void injectAndNotify(final T instance,
|
||||
final Errors errors,
|
||||
final Key<T> key, // possibly null!
|
||||
final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
|
||||
final Object source,
|
||||
final boolean toolableOnly) throws ErrorsException {
|
||||
final boolean toolableOnly) throws InternalProvisionException {
|
||||
if (instance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
injector.callInContext(new ContextualCallable<Void>() {
|
||||
@Override
|
||||
public Void call(final InternalContext context) throws ErrorsException {
|
||||
context.pushState(key, source);
|
||||
try {
|
||||
if (provisionCallback != null && provisionCallback.hasListeners()) {
|
||||
provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
||||
@Override
|
||||
public T call() {
|
||||
injectMembers(instance, errors, context, toolableOnly);
|
||||
return instance;
|
||||
}
|
||||
final InternalContext context = injector.enterContext();
|
||||
context.pushState(key, source);
|
||||
try {
|
||||
if (provisionCallback != null && provisionCallback.hasListeners()) {
|
||||
provisionCallback.provision(context, () -> {
|
||||
injectMembers(instance, context, toolableOnly);
|
||||
return instance;
|
||||
});
|
||||
} else {
|
||||
injectMembers(instance, errors, context, toolableOnly);
|
||||
}
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
injectMembers(instance, context, toolableOnly);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
context.popState();
|
||||
context.close();
|
||||
}
|
||||
|
||||
// TODO: We *could* notify listeners too here,
|
||||
// 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
|
||||
// anything.)
|
||||
if (!toolableOnly) {
|
||||
notifyListeners(instance, errors);
|
||||
notifyListeners(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyListeners(T instance, Errors errors) throws ErrorsException {
|
||||
int numErrorsBefore = errors.size();
|
||||
for (InjectionListener<? super T> injectionListener : injectionListeners) {
|
||||
void notifyListeners(T instance) throws InternalProvisionException {
|
||||
ImmutableList<InjectionListener<? super T>> localInjectionListeners = 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 {
|
||||
injectionListener.afterInjection(instance);
|
||||
} 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) {
|
||||
// optimization: use manual for/each to save allocating an iterator here
|
||||
for (int i = 0, size = memberInjectors.size(); i < size; i++) {
|
||||
SingleMemberInjector injector = memberInjectors.get(i);
|
||||
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
|
||||
injector.inject(errors, context, t);
|
||||
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
|
||||
for (int i = 0, size = localMembersInjectors.size(); i < size; i++) {
|
||||
SingleMemberInjector injector = localMembersInjectors.get(i);
|
||||
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
|
||||
injector.inject(context, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: There's no way to know if a user's MembersInjector wants toolable injections.
|
||||
if (!toolableOnly) {
|
||||
for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) {
|
||||
try {
|
||||
userMembersInjector.injectMembers(t);
|
||||
} catch (RuntimeException e) {
|
||||
errors.errorInUserInjector(userMembersInjector, typeLiteral, e);
|
||||
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 {
|
||||
userMembersInjector.injectMembers(t);
|
||||
} catch (RuntimeException e) {
|
||||
throw InternalProvisionException.errorInUserInjector(
|
||||
userMembersInjector, typeLiteral, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,11 +144,15 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
|
|||
}
|
||||
|
||||
public ImmutableSet<InjectionPoint> getInjectionPoints() {
|
||||
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||
builder.add(memberInjector.getInjectionPoint());
|
||||
ImmutableList<SingleMemberInjector> localMemberInjectors = memberInjectors;
|
||||
if (localMemberInjectors != null) {
|
||||
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||
for (SingleMemberInjector memberInjector : localMemberInjectors) {
|
||||
builder.add(memberInjector.getInjectionPoint());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return builder.build();
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.google.inject.internal;
|
||||
|
||||
import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.ConfigurationException;
|
||||
|
@ -11,24 +14,25 @@ import com.google.inject.spi.TypeListenerBinding;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Members injectors by type.
|
||||
*/
|
||||
final class MembersInjectorStore {
|
||||
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private final ImmutableList<TypeListenerBinding> typeListenerBindings;
|
||||
|
||||
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache
|
||||
= new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() {
|
||||
@Override
|
||||
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
|
||||
throws ErrorsException {
|
||||
return createWithListeners(type, errors);
|
||||
}
|
||||
};
|
||||
|
||||
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache = new FailableCache<>() {
|
||||
@Override
|
||||
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
|
||||
throws ErrorsException {
|
||||
return createWithListeners(type, errors);
|
||||
}
|
||||
};
|
||||
MembersInjectorStore(InjectorImpl injector,
|
||||
List<TypeListenerBinding> typeListenerBindings) {
|
||||
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
|
||||
* aren't any type listeners.
|
||||
*/
|
||||
public boolean hasTypeListeners() {
|
||||
boolean hasTypeListeners() {
|
||||
return !typeListenerBindings.isEmpty();
|
||||
}
|
||||
|
||||
|
@ -81,7 +85,7 @@ final class MembersInjectorStore {
|
|||
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
|
||||
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();
|
||||
for (TypeListenerBinding binding : typeListenerBindings) {
|
||||
TypeListener typeListener = binding.getListener();
|
||||
|
@ -97,7 +101,7 @@ final class MembersInjectorStore {
|
|||
encounter.invalidate();
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static String getRootMessage(Throwable t) {
|
||||
Throwable cause = t.getCause();
|
||||
return cause == null ? t.toString() : getRootMessage(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
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
|
||||
if (toResolve.isInterface()) {
|
||||
Class[] interfaces = rawType.getInterfaces();
|
||||
Class<?>[] interfaces = rawType.getInterfaces();
|
||||
for (int i = 0, length = interfaces.length; i < length; i++) {
|
||||
if (interfaces[i] == toResolve) {
|
||||
return rawType.getGenericInterfaces()[i];
|
||||
|
@ -291,7 +291,7 @@ public class MoreTypes {
|
|||
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);
|
||||
|
||||
// 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
|
||||
* a class.
|
||||
*/
|
||||
private static Class<?> declaringClassOf(TypeVariable typeVariable) {
|
||||
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
|
||||
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
|
||||
return genericDeclaration instanceof Class
|
||||
? (Class<?>) genericDeclaration
|
||||
|
@ -365,7 +365,7 @@ public class MoreTypes {
|
|||
|
||||
private static void ensureOwnerType(Type ownerType, Type rawType) {
|
||||
if (rawType instanceof Class<?>) {
|
||||
Class rawTypeAsClass = (Class) rawType;
|
||||
Class<?> rawTypeAsClass = (Class) rawType;
|
||||
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
|
||||
"No owner type for enclosed %s", rawType);
|
||||
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
|
||||
|
@ -373,18 +373,22 @@ public class MoreTypes {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getActualTypeArguments() {
|
||||
return typeArguments.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return ownerType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullySpecified() {
|
||||
if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
|
||||
return false;
|
||||
|
@ -441,10 +445,12 @@ public class MoreTypes {
|
|||
this.componentType = canonicalize(componentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getGenericComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullySpecified() {
|
||||
return MoreTypes.isFullySpecified(componentType);
|
||||
}
|
||||
|
@ -495,14 +501,17 @@ public class MoreTypes {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getUpperBounds() {
|
||||
return new Type[]{upperBound};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getLowerBounds() {
|
||||
return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullySpecified() {
|
||||
return MoreTypes.isFullySpecified(upperBound)
|
||||
&& (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
|
||||
|
|
|
@ -25,7 +25,7 @@ final class PrivateElementProcessor extends AbstractProcessor {
|
|||
return true;
|
||||
}
|
||||
|
||||
public List<InjectorShell.Builder> getInjectorShellBuilders() {
|
||||
List<InjectorShell.Builder> getInjectorShellBuilders() {
|
||||
return injectorShellBuilders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,10 +49,12 @@ public final class PrivateElementsImpl implements PrivateElements {
|
|||
this.source = checkNotNull(source, "source");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Element> getElements() {
|
||||
if (elements == null) {
|
||||
elements = ImmutableList.copyOf(elementsMutable);
|
||||
|
@ -62,15 +64,17 @@ public final class PrivateElementsImpl implements PrivateElements {
|
|||
return elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector getInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
public void initInjector(Injector injector) {
|
||||
void initInjector(Injector injector) {
|
||||
checkState(this.injector == null, "injector already initialized");
|
||||
this.injector = checkNotNull(injector, "injector");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Key<?>> getExposedKeys() {
|
||||
if (exposedKeysToSources == null) {
|
||||
Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap();
|
||||
|
@ -84,6 +88,7 @@ public final class PrivateElementsImpl implements PrivateElements {
|
|||
return exposedKeysToSources.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
@ -96,6 +101,7 @@ public final class PrivateElementsImpl implements PrivateElements {
|
|||
exposureBuilders.add(exposureBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(Binder binder) {
|
||||
PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder();
|
||||
|
||||
|
@ -109,6 +115,7 @@ public final class PrivateElementsImpl implements PrivateElements {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getExposedSource(Key<?> key) {
|
||||
getExposedKeys(); // ensure exposedKeysToSources is populated
|
||||
Object source = exposedKeysToSources.get(key);
|
||||
|
|
|
@ -11,8 +11,11 @@ import java.util.List;
|
|||
class ProcessedBindingData {
|
||||
|
||||
private final List<CreationListener> creationListeners = Lists.newArrayList();
|
||||
|
||||
private final List<Runnable> uninitializedBindings = Lists.newArrayList();
|
||||
|
||||
private final List<Runnable> delayedUninitializedBindings = Lists.newArrayList();
|
||||
|
||||
void addCreationListener(CreationListener listener) {
|
||||
creationListeners.add(listener);
|
||||
}
|
||||
|
@ -21,6 +24,10 @@ class ProcessedBindingData {
|
|||
uninitializedBindings.add(runnable);
|
||||
}
|
||||
|
||||
void addDelayedUninitializedBinding(Runnable runnable) {
|
||||
delayedUninitializedBindings.add(runnable);
|
||||
}
|
||||
|
||||
void initializeBindings() {
|
||||
for (Runnable initializer : uninitializedBindings) {
|
||||
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.ProvidedBy;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.internal.InjectorImpl.JitLimitation;
|
||||
import com.google.inject.spi.Dependency;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings.
|
||||
|
@ -34,40 +32,48 @@ class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
|
|||
provisionCallback = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||
providerBinding =
|
||||
injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
checkState(providerBinding != null, "not initialized");
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
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 {
|
||||
errors = errors.withSource(providerKey);
|
||||
Provider<? extends T> provider = providerBinding.getInternalFactory().get(
|
||||
errors, context, dependency, true);
|
||||
return circularGet(provider, errors, context, dependency, provisionCallback);
|
||||
Provider<? extends T> provider =
|
||||
localProviderBinding.getInternalFactory().get(context, dependency, true);
|
||||
return circularGet(provider, context, dependency, provisionCallback);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(localProviderKey);
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
|
||||
Dependency<?> dependency, ConstructionContext<T> constructionContext)
|
||||
throws ErrorsException {
|
||||
protected T provision(javax.inject.Provider<? extends T> provider,
|
||||
Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext)
|
||||
throws InternalProvisionException {
|
||||
try {
|
||||
Object o = super.provision(provider, errors, dependency, constructionContext);
|
||||
Object o = super.provision(provider, dependency, constructionContext);
|
||||
if (o != null && !rawType.isInstance(o)) {
|
||||
throw errors.subtypeNotProvided(providerType, rawType).toException();
|
||||
throw InternalProvisionException.subtypeNotProvided(providerType, rawType);
|
||||
}
|
||||
@SuppressWarnings("unchecked") // protected by isInstance() check above
|
||||
T t = (T) o;
|
||||
T t = (T) o;
|
||||
return t;
|
||||
} 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;
|
||||
|
||||
final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
|
||||
implements ProviderInstanceBinding<T> {
|
||||
class ProviderInstanceBindingImpl<T> extends BindingImpl<T> implements ProviderInstanceBinding<T> {
|
||||
|
||||
final javax.inject.Provider<? extends T> providerInstance;
|
||||
final ImmutableSet<InjectionPoint> injectionPoints;
|
||||
private final javax.inject.Provider<? extends T> providerInstance;
|
||||
|
||||
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,
|
||||
javax.inject.Provider<? extends T> providerInstance,
|
||||
Set<InjectionPoint> injectionPoints) {
|
||||
|
@ -29,7 +29,7 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
|
|||
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) {
|
||||
super(source, key, scoping);
|
||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||
|
@ -61,13 +61,11 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
|
|||
}
|
||||
|
||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||
return new ProviderInstanceBindingImpl<T>(
|
||||
getSource(), getKey(), scoping, injectionPoints, providerInstance);
|
||||
return new ProviderInstanceBindingImpl<>(getSource(), getKey(), scoping, injectionPoints, providerInstance);
|
||||
}
|
||||
|
||||
public BindingImpl<T> withKey(Key<T> key) {
|
||||
return new ProviderInstanceBindingImpl<T>(
|
||||
getSource(), key, getScoping(), injectionPoints, providerInstance);
|
||||
return new ProviderInstanceBindingImpl<>(getSource(), key, getScoping(), injectionPoints, providerInstance);
|
||||
}
|
||||
|
||||
public void applyTo(Binder binder) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.google.inject.internal;
|
||||
|
||||
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||
import com.google.inject.spi.Dependency;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
@ -20,33 +19,28 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
|
|||
this.source = checkNotNull(source, "source");
|
||||
}
|
||||
|
||||
protected T circularGet(final Provider<? extends T> provider, final Errors errors,
|
||||
InternalContext context, final Dependency<?> dependency,
|
||||
protected T circularGet(final Provider<? extends T> provider,
|
||||
InternalContext context,
|
||||
final Dependency<?> dependency,
|
||||
ProvisionListenerStackCallback<T> provisionCallback)
|
||||
throws ErrorsException {
|
||||
throws InternalProvisionException {
|
||||
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||
|
||||
// We have a circular reference between constructors. Return a proxy.
|
||||
if (constructionContext.isConstructing()) {
|
||||
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
|
||||
// TODO: if we can't proxy this object, can we proxy the other object?
|
||||
@SuppressWarnings("unchecked")
|
||||
T proxyType = (T) constructionContext.createProxy(
|
||||
errors, context.getInjectorOptions(), expectedType);
|
||||
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.hasListeners()) {
|
||||
return provision(provider, errors, dependency, constructionContext);
|
||||
return provision(provider, dependency, constructionContext);
|
||||
} else {
|
||||
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
||||
public T call() throws ErrorsException {
|
||||
return provision(provider, errors, dependency, constructionContext);
|
||||
}
|
||||
});
|
||||
return provisionCallback.provision(context, () ->
|
||||
provision(provider, dependency, constructionContext));
|
||||
}
|
||||
} finally {
|
||||
constructionContext.removeCurrentReference();
|
||||
|
@ -58,9 +52,12 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
|
|||
* Provisions a new instance. Subclasses should override this to catch
|
||||
* exceptions & rethrow as ErrorsExceptions.
|
||||
*/
|
||||
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||
T t = errors.checkForNull(provider.get(), source, dependency);
|
||||
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||
T t = provider.get();
|
||||
if (t == null && !dependency.isNullable()) {
|
||||
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
|
||||
}
|
||||
constructionContext.setProxyDelegates(t);
|
||||
return t;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.google.inject.Binder;
|
|||
import com.google.inject.Exposed;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.PrivateBinder;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.internal.util.StackTraceElements;
|
||||
import com.google.inject.spi.BindingTargetVisitor;
|
||||
|
@ -21,36 +20,50 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A provider that invokes a method and returns its result.
|
||||
*
|
||||
*/
|
||||
public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
|
||||
ProvidesMethodBinding<T> {
|
||||
public abstract class ProviderMethod<T>
|
||||
extends InternalProviderInstanceBindingImpl.CyclicFactory<T>
|
||||
implements HasDependencies, ProviderWithExtensionVisitor<T>, ProvidesMethodBinding<T> {
|
||||
|
||||
protected final Object instance;
|
||||
|
||||
protected final Method method;
|
||||
|
||||
private final Key<T> key;
|
||||
|
||||
private final Class<? extends Annotation> scopeAnnotation;
|
||||
|
||||
private final ImmutableSet<Dependency<?>> dependencies;
|
||||
private final List<Provider<?>> parameterProviders;
|
||||
|
||||
private final boolean exposed;
|
||||
|
||||
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}.
|
||||
*/
|
||||
private ProviderMethod(Key<T> key, Method method, Object instance,
|
||||
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
|
||||
Class<? extends Annotation> scopeAnnotation, Annotation annotation) {
|
||||
private ProviderMethod(Key<T> key,
|
||||
Method method,
|
||||
Object instance,
|
||||
ImmutableSet<Dependency<?>> dependencies,
|
||||
Class<? extends Annotation> scopeAnnotation,
|
||||
Annotation annotation) {
|
||||
super(InternalProviderInstanceBindingImpl.InitializationTiming.EAGER);
|
||||
this.key = key;
|
||||
this.scopeAnnotation = scopeAnnotation;
|
||||
this.instance = instance;
|
||||
this.dependencies = dependencies;
|
||||
this.method = method;
|
||||
this.parameterProviders = parameterProviders;
|
||||
this.exposed = method.isAnnotationPresent(Exposed.class);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
@ -58,24 +71,19 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
|
|||
/**
|
||||
* Creates a {@link ProviderMethod}.
|
||||
*/
|
||||
static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance,
|
||||
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
|
||||
Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration,
|
||||
static <T> ProviderMethod<T> create(Key<T> key,
|
||||
Method method,
|
||||
Object instance,
|
||||
ImmutableSet<Dependency<?>> dependencies,
|
||||
Class<? extends Annotation> scopeAnnotation,
|
||||
boolean skipFastClassGeneration,
|
||||
Annotation annotation) {
|
||||
int modifiers = method.getModifiers();
|
||||
|
||||
if (!Modifier.isPublic(modifiers) ||
|
||||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
|
||||
return new ReflectionProviderMethod<T>(key,
|
||||
method,
|
||||
instance,
|
||||
dependencies,
|
||||
parameterProviders,
|
||||
scopeAnnotation,
|
||||
annotation);
|
||||
return new ReflectionProviderMethod<>(key, method, instance, dependencies, scopeAnnotation, annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,27 +128,31 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
|
|||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
Object[] parameters = new Object[parameterProviders.size()];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
parameters[i] = parameterProviders.get(i).get();
|
||||
}
|
||||
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||
parameterInjectors = injector.getParametersInjectors(dependencies.asList(), errors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T doProvision(InternalContext context, Dependency<?> dependency)
|
||||
throws InternalProvisionException {
|
||||
try {
|
||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
||||
T result = (T) doProvision(parameters);
|
||||
return result;
|
||||
T t = doProvision(SingleParameterInjector.getAll(context, parameterInjectors));
|
||||
if (t == null && !dependency.isNullable()) {
|
||||
InternalProvisionException.onNullInjectedIntoNonNullableDependency(getMethod(), dependency);
|
||||
}
|
||||
return t;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Exceptions.rethrowCause(e);
|
||||
} catch (InvocationTargetException userException) {
|
||||
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
|
||||
throw InternalProvisionException.errorInProvider(cause).addSource(getSource());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point for our subclasses to implement the provisioning strategy.
|
||||
*/
|
||||
abstract Object doProvision(Object[] parameters)
|
||||
abstract T doProvision(Object[] parameters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
@Override
|
||||
|
@ -199,22 +211,21 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
|
|||
Method method,
|
||||
Object instance,
|
||||
ImmutableSet<Dependency<?>> dependencies,
|
||||
List<Provider<?>> parameterProviders,
|
||||
Class<? extends Annotation> scopeAnnotation,
|
||||
Annotation annotation) {
|
||||
super(key,
|
||||
method,
|
||||
instance,
|
||||
dependencies,
|
||||
parameterProviders,
|
||||
scopeAnnotation,
|
||||
annotation);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
Object doProvision(Object[] parameters) throws IllegalAccessException,
|
||||
T doProvision(Object[] parameters) throws IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
return method.invoke(instance, parameters);
|
||||
return (T) method.invoke(instance, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package com.google.inject.internal;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.Message;
|
||||
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
|
||||
|
@ -21,11 +18,12 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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
|
||||
|
@ -34,23 +32,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
*/
|
||||
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 TypeLiteral<?> typeLiteral;
|
||||
|
||||
private final boolean skipFastClassGeneration;
|
||||
|
||||
private final ModuleAnnotatedMethodScanner scanner;
|
||||
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
|
@ -92,143 +79,143 @@ public final class ProviderMethodsModule implements Module {
|
|||
if (object instanceof ProviderMethodsModule) {
|
||||
return Modules.EMPTY_MODULE;
|
||||
}
|
||||
|
||||
return new ProviderMethodsModule(object, skipFastClassGeneration, scanner);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public Class<?> getDelegateModuleClass() {
|
||||
return isStaticModule() ? (Class<?>) delegate : delegate.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
|
||||
providerMethod.configure(binder);
|
||||
}
|
||||
if (Modifier.isPrivate(modifiers)) {
|
||||
return false;
|
||||
}
|
||||
// b must be package-private
|
||||
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
|
||||
}
|
||||
|
||||
private boolean isStaticModule() {
|
||||
return delegate instanceof Class;
|
||||
}
|
||||
|
||||
public Object getDelegateModule() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void configure(Binder binder) {
|
||||
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
|
||||
providerMethod.configure(binder);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ProviderMethod<?>> getProviderMethods(Binder binder) {
|
||||
List<ProviderMethod<?>> result = Lists.newArrayList();
|
||||
Multimap<Signature, Method> methodsBySignature = HashMultimap.create();
|
||||
for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) {
|
||||
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)
|
||||
&& !method.isBridge() && !method.isSynthetic()) {
|
||||
methodsBySignature.put(new Signature(method), method);
|
||||
}
|
||||
Optional<Annotation> annotation = isProvider(binder, method);
|
||||
if (annotation.isPresent()) {
|
||||
result.add(createProviderMethod(binder, method, annotation.get()));
|
||||
List<ProviderMethod<?>> result = null;
|
||||
List<MethodAndAnnotation> methodsAndAnnotations = null;
|
||||
// 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<>();
|
||||
}
|
||||
|
||||
ProviderMethod<Object> providerMethod = createProviderMethod(binder, method, annotation);
|
||||
if (providerMethod != null) {
|
||||
result.add(providerMethod);
|
||||
}
|
||||
methodsAndAnnotations.add(new MethodAndAnnotation(method, annotation));
|
||||
superMostClass = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
// we have found all the providers and now need to identify if any were overridden
|
||||
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is only
|
||||
// assuming that every method is an override, in general it should be very quick.
|
||||
for (ProviderMethod<?> provider : result) {
|
||||
Method method = provider.getMethod();
|
||||
for (Method matchingSignature : methodsBySignature.get(new Signature(method))) {
|
||||
// matching signature is in the same class or a super class, therefore method cannot be
|
||||
// overridding it.
|
||||
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
|
||||
continue;
|
||||
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()) {
|
||||
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0)
|
||||
&& !method.isBridge()
|
||||
&& !method.isSynthetic()) {
|
||||
if (methodsBySignature == null) {
|
||||
methodsBySignature = HashMultimap.create();
|
||||
}
|
||||
methodsBySignature.put(new Signature(typeLiteral, method), method);
|
||||
}
|
||||
// now we know matching signature is in a subtype of method.getDeclaringClass()
|
||||
if (overrides(matchingSignature, method)) {
|
||||
String annotationString = provider.getAnnotation().annotationType() == Provides.class
|
||||
? "@Provides" : "@" + provider.getAnnotation().annotationType().getCanonicalName();
|
||||
binder.addError(
|
||||
"Overriding " + annotationString + " methods is not allowed."
|
||||
+ "\n\t" + annotationString + " method: %s\n\toverridden by: %s",
|
||||
method,
|
||||
matchingSignature);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (methodsBySignature != null) {
|
||||
// we have found all the signatures and now need to identify if any were overridden
|
||||
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is
|
||||
// only assuming that every method is an override, in general it should be very quick.
|
||||
for (MethodAndAnnotation methodAndAnnotation : methodsAndAnnotations) {
|
||||
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
|
||||
// overridding it.
|
||||
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
|
||||
continue;
|
||||
}
|
||||
// now we know matching signature is in a subtype of method.getDeclaringClass()
|
||||
if (overrides(matchingSignature, method)) {
|
||||
String annotationString =
|
||||
annotation.annotationType() == Provides.class
|
||||
? "@Provides"
|
||||
: "@" + annotation.annotationType().getCanonicalName();
|
||||
binder.addError(
|
||||
"Overriding "
|
||||
+ annotationString
|
||||
+ " methods is not allowed."
|
||||
+ "\n\t"
|
||||
+ annotationString
|
||||
+ " method: %s\n\toverridden by: %s",
|
||||
method,
|
||||
matchingSignature);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the method is a provider.
|
||||
*
|
||||
* 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) {
|
||||
/** Returns the annotation that is claimed by the scanner, or null if there is none. */
|
||||
private Annotation getAnnotation(Binder binder, Method method) {
|
||||
if (method.isBridge() || method.isSynthetic()) {
|
||||
return Optional.absent();
|
||||
return null;
|
||||
}
|
||||
Annotation annotation = null;
|
||||
for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) {
|
||||
Annotation foundAnnotation = method.getAnnotation(annotationClass);
|
||||
if (foundAnnotation != 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.",
|
||||
scanner, method);
|
||||
return Optional.absent();
|
||||
return null;
|
||||
}
|
||||
annotation = foundAnnotation;
|
||||
}
|
||||
}
|
||||
return Optional.fromNullable(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);
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -243,12 +230,23 @@ public final class ProviderMethodsModule implements Module {
|
|||
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 String name;
|
||||
final int hashCode;
|
||||
|
||||
Signature(Method method) {
|
||||
Signature(TypeLiteral<?> typeLiteral, Method method) {
|
||||
this.name = method.getName();
|
||||
// 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
|
||||
|
@ -278,4 +276,75 @@ public final class ProviderMethodsModule implements Module {
|
|||
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;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.spi.Dependency;
|
||||
|
||||
final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
|
||||
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private final InternalFactory<? extends T> internalFactory;
|
||||
|
||||
public ProviderToInternalFactoryAdapter(InjectorImpl injector,
|
||||
|
@ -15,25 +14,23 @@ final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
|
|||
this.internalFactory = internalFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
final Errors errors = new Errors();
|
||||
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
|
||||
// scoping implicit bindings. If we are not actually a linked
|
||||
// binding, we'll fail properly elsewhere in the chain.
|
||||
return internalFactory.get(errors, context, dependency, true);
|
||||
}
|
||||
});
|
||||
errors.throwIfNewErrors(0);
|
||||
return t;
|
||||
} catch (ErrorsException e) {
|
||||
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
|
||||
try (InternalContext context = injector.enterContext()) {
|
||||
// Always pretend that we are a linked binding, to support
|
||||
// scoping implicit bindings. If we are not actually a linked
|
||||
// binding, we'll fail properly elsewhere in the chain.
|
||||
return internalFactory.get(context, context.getDependency(), true);
|
||||
} catch (InternalProvisionException e) {
|
||||
throw e.toProvisionException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Exposed for SingletonScope. */
|
||||
InjectorImpl getInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return internalFactory.toString();
|
||||
|
|
|
@ -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 LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache
|
||||
= CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() {
|
||||
private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public ProvisionListenerStackCallback<?> load(KeyBinding key) {
|
||||
return create(key.binding);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package com.google.inject.internal;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Binding;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.spi.DependencyAndSource;
|
||||
import com.google.inject.spi.ProvisionListener;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -16,7 +14,8 @@ import java.util.Set;
|
|||
*/
|
||||
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"})
|
||||
private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
|
||||
new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of());
|
||||
|
@ -43,9 +42,9 @@ final class ProvisionListenerStackCallback<T> {
|
|||
return listeners.length > 0;
|
||||
}
|
||||
|
||||
public T provision(Errors errors, InternalContext context, ProvisionCallback<T> callable)
|
||||
throws ErrorsException {
|
||||
Provision provision = new Provision(errors, context, callable);
|
||||
public T provision(InternalContext context, ProvisionCallback<T> callable)
|
||||
throws InternalProvisionException {
|
||||
Provision provision = new Provision(context, callable);
|
||||
RuntimeException caught = null;
|
||||
try {
|
||||
provision.provision();
|
||||
|
@ -56,12 +55,14 @@ final class ProvisionListenerStackCallback<T> {
|
|||
if (provision.exceptionDuringProvision != null) {
|
||||
throw provision.exceptionDuringProvision;
|
||||
} else if (caught != null) {
|
||||
Object listener = provision.erredListener != null ?
|
||||
provision.erredListener.getClass() : "(unknown)";
|
||||
throw errors
|
||||
.errorInUserCode(caught, "Error notifying ProvisionListener %s of %s.%n"
|
||||
+ " Reason: %s", listener, binding.getKey(), caught)
|
||||
.toException();
|
||||
Object listener =
|
||||
provision.erredListener != null ? provision.erredListener.getClass() : "(unknown)";
|
||||
throw InternalProvisionException.errorInUserCode(
|
||||
caught,
|
||||
"Error notifying ProvisionListener %s of %s.%n Reason: %s",
|
||||
listener,
|
||||
binding.getKey(),
|
||||
caught);
|
||||
} else {
|
||||
return provision.result;
|
||||
}
|
||||
|
@ -69,25 +70,21 @@ final class ProvisionListenerStackCallback<T> {
|
|||
|
||||
// TODO(sameb): Can this be more InternalFactory-like?
|
||||
public interface ProvisionCallback<T> {
|
||||
T call() throws ErrorsException;
|
||||
T call() throws InternalProvisionException;
|
||||
}
|
||||
|
||||
private class Provision extends ProvisionListener.ProvisionInvocation<T> {
|
||||
|
||||
final Errors errors;
|
||||
final int numErrorsBefore;
|
||||
final InternalContext context;
|
||||
final ProvisionCallback<T> callable;
|
||||
int index = -1;
|
||||
T result;
|
||||
ErrorsException exceptionDuringProvision;
|
||||
InternalProvisionException exceptionDuringProvision;
|
||||
ProvisionListener erredListener;
|
||||
|
||||
public Provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) {
|
||||
public Provision(InternalContext context, ProvisionCallback<T> callable) {
|
||||
this.callable = callable;
|
||||
this.context = context;
|
||||
this.errors = errors;
|
||||
this.numErrorsBefore = errors.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,12 +93,9 @@ final class ProvisionListenerStackCallback<T> {
|
|||
if (index == listeners.length) {
|
||||
try {
|
||||
result = callable.call();
|
||||
// Make sure we don't return the provisioned object if there were any errors
|
||||
// injecting its field/method dependencies.
|
||||
errors.throwIfNewErrors(numErrorsBefore);
|
||||
} catch (ErrorsException ee) {
|
||||
exceptionDuringProvision = ee;
|
||||
throw new ProvisionException(errors.merge(ee.getErrors()).getMessages());
|
||||
} catch (InternalProvisionException ipe) {
|
||||
exceptionDuringProvision = ipe;
|
||||
throw ipe.toProvisionException();
|
||||
}
|
||||
} else if (index < listeners.length) {
|
||||
int currentIdx = index;
|
||||
|
@ -128,10 +122,5 @@ final class ProvisionListenerStackCallback<T> {
|
|||
// if someone calls that they'll get strange errors.
|
||||
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.internal.Annotations;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
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();
|
||||
|
||||
Provider<T> scoped
|
||||
= scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
|
||||
Provider<T> scoped = scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
|
||||
return new InternalFactoryToProviderAdapter<T>(scoped, source);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,35 +10,38 @@ import java.lang.reflect.Field;
|
|||
* Sets an injectable field.
|
||||
*/
|
||||
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 {
|
||||
this.injectionPoint = injectionPoint;
|
||||
this.field = (Field) injectionPoint.getMember();
|
||||
this.dependency = injectionPoint.getDependencies().get(0);
|
||||
|
||||
// Ewwwww...
|
||||
field.setAccessible(true);
|
||||
binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InjectionPoint getInjectionPoint() {
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
public void inject(Errors errors, InternalContext context, Object o) {
|
||||
errors = errors.withSource(dependency);
|
||||
|
||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
||||
@Override
|
||||
public void inject(InternalContext context, Object o) throws InternalProvisionException {
|
||||
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
|
||||
try {
|
||||
Object value = binding.getInternalFactory().get(errors, context, dependency, false);
|
||||
Object value = binding.getInternalFactory().get(context, dependency, false);
|
||||
field.set(o, value);
|
||||
} catch (ErrorsException e) {
|
||||
errors.withSource(injectionPoint).merge(e.getErrors());
|
||||
} catch (InternalProvisionException e) {
|
||||
throw e.addSource(dependency);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||
} finally {
|
||||
|
|
|
@ -6,7 +6,7 @@ import com.google.inject.spi.InjectionPoint;
|
|||
* Injects a field or method of a given object.
|
||||
*/
|
||||
interface SingleMemberInjector {
|
||||
void inject(Errors errors, InternalContext context, Object o);
|
||||
void inject(InternalContext context, Object o) throws InternalProvisionException;
|
||||
|
||||
InjectionPoint getInjectionPoint();
|
||||
}
|
||||
|
|
|
@ -29,37 +29,23 @@ final class SingleMethodInjector implements SingleMemberInjector {
|
|||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
|
||||
return new MethodInvoker() {
|
||||
public Object invoke(Object target, Object... parameters)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
return method.invoke(target, parameters);
|
||||
}
|
||||
};
|
||||
return method::invoke;
|
||||
}
|
||||
|
||||
public InjectionPoint getInjectionPoint() {
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
public void inject(Errors errors, InternalContext context, Object o) {
|
||||
Object[] parameters;
|
||||
try {
|
||||
parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(InternalContext context, Object o) throws InternalProvisionException {
|
||||
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
|
||||
try {
|
||||
methodInvoker.invoke(o, parameters);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||
} catch (InvocationTargetException userException) {
|
||||
Throwable cause = userException.getCause() != null
|
||||
? userException.getCause()
|
||||
: userException;
|
||||
errors.withSource(injectionPoint).errorInjectingMethod(cause);
|
||||
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
|
||||
throw InternalProvisionException.errorInjectingMethod(cause).addSource(injectionPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,48 +6,47 @@ import com.google.inject.spi.Dependency;
|
|||
* Resolves a single parameter, to be used in a constructor or method invocation.
|
||||
*/
|
||||
final class SingleParameterInjector<T> {
|
||||
|
||||
private static final Object[] NO_ARGUMENTS = {};
|
||||
|
||||
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) {
|
||||
this.dependency = dependency;
|
||||
this.binding = binding;
|
||||
this.source = binding.getSource();
|
||||
this.factory = binding.getInternalFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of parameter values.
|
||||
*/
|
||||
static Object[] getAll(Errors errors, InternalContext context,
|
||||
SingleParameterInjector<?>[] parameterInjectors) throws ErrorsException {
|
||||
static Object[] getAll(InternalContext context,
|
||||
SingleParameterInjector<?>[] parameterInjectors) throws InternalProvisionException {
|
||||
if (parameterInjectors == null) {
|
||||
return NO_ARGUMENTS;
|
||||
}
|
||||
|
||||
int numErrorsBefore = errors.size();
|
||||
|
||||
int size = parameterInjectors.length;
|
||||
Object[] parameters = new Object[size];
|
||||
|
||||
// optimization: use manual for/each to save allocating an iterator here
|
||||
for (int i = 0; i < size; i++) {
|
||||
SingleParameterInjector<?> parameterInjector = parameterInjectors[i];
|
||||
try {
|
||||
parameters[i] = parameterInjector.inject(errors, context);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
parameters[i] = parameterInjectors[i].inject(context);
|
||||
}
|
||||
|
||||
errors.throwIfNewErrors(numErrorsBefore);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private T inject(Errors errors, InternalContext context) throws ErrorsException {
|
||||
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
||||
T inject(InternalContext context) throws InternalProvisionException {
|
||||
Dependency<T> localDependency = dependency;
|
||||
Dependency<?> previous = context.pushDependency(localDependency, source);
|
||||
|
||||
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 {
|
||||
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